Я хочу, чтобы иметь возможность приближать точку под мышью в холсте HTML 5, например масштабирование на Google Maps. Как я могу это достичь?Увеличение масштаба (используя масштаб и перевод)
ответ
Наконец решил его:
var zoomIntensity = 0.2;
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = 600;
var height = 200;
var scale = 1;
var originx = 0;
var originy = 0;
var visibleWidth = width;
var visibleHeight = height;
function draw(){
// Clear screen to white.
context.fillStyle = "white";
context.fillRect(originx,originy,800/scale,600/scale);
// Draw the black square.
context.fillStyle = "black";
context.fillRect(50,50,100,100);
}
// Draw loop at 60FPS.
setInterval(draw, 1000/60);
canvas.onmousewheel = function (event){
event.preventDefault();
// Get mouse offset.
var mousex = event.clientX - canvas.offsetLeft;
var mousey = event.clientY - canvas.offsetTop;
// Normalize wheel to +1 or -1.
var wheel = event.wheelDelta/120;
// Compute zoom factor.
var zoom = Math.exp(wheel*zoomIntensity);
// Translate so the visible origin is at the context's origin.
context.translate(originx, originy);
// Compute the new visible origin. Originally the mouse is at a
// distance mouse/scale from the corner, we want the point under
// the mouse to remain in the same place after the zoom, but this
// is at mouse/new_scale away from the corner. Therefore we need to
// shift the origin (coordinates of the corner) to account for this.
originx -= mousex/(scale*zoom) - mousex/scale;
originy -= mousey/(scale*zoom) - mousey/scale;
// Scale it (centered around the origin due to the trasnslate above).
context.scale(zoom, zoom);
// Offset the visible origin to it's proper position.
context.translate(-originx, -originy);
// Update scale and others.
scale *= zoom;
visibleWidth = width/scale;
visibleHeight = height/scale;
}
<canvas id="canvas" width="600" height="200"></canvas>
Ключ, как @Tatarize указал, чтобы вычислить положение оси таким образом, что точка масштабирования (указатель мыши) остается в том же месте после увеличения.
Первоначально мышь находится на расстоянии mouse/scale
от угла, мы хотим, чтобы точка под мышью оставалась на том же месте после зума, но это mouse/new_scale
в стороне от угла. Поэтому для этого необходимо сдвинуть origin
(координаты угла).
originx -= mousex/(scale*zoom) - mousex/scale;
originy -= mousey/(scale*zoom) - mousey/scale;
scale *= zomm
Остальной код, то необходимо применить масштабирование и перевести к контексту дро, так что это начало совпадает с брезентовой углу.
Это на самом деле очень сложная проблема (математически), и я работаю над одним и тем же почти. Я задал аналогичный вопрос в Stackoverflow, но не получил ответа, но отправлен в DocType (StackOverflow для HTML/CSS) и получил ответ. Проверьте это http://doctype.com/javascript-image-zoom-css3-transforms-calculate-origin-example
Я нахожусь в центре создания плагина jQuery, который делает это (масштабирование в стиле Google Maps с использованием CSS3 Transforms). У меня есть стрелка увеличения до мыши, работающая нормально, все еще пытаясь понять, как разрешить пользователю перетаскивать холст, как вы можете делать в Google Maps. Когда я получу его работу, я отправлю код здесь, но ознакомьтесь с ссылкой выше для части «мышь-зум-точка».
Я не понимал, что существует масштаб и перевод методов в контексте Canvas, вы можете добиться того же, используя CSS3, например. с помощью JQuery:
$('div.canvasContainer > canvas')
.css('-moz-transform', 'scale(1) translate(0px, 0px)')
.css('-webkit-transform', 'scale(1) translate(0px, 0px)')
.css('-o-transform', 'scale(1) translate(0px, 0px)')
.css('transform', 'scale(1) translate(0px, 0px)');
Убедитесь, что вы установили CSS3 преобразования происхождения 0, 0 (-moz-преобразование-происхождения: 0 0). Используя преобразование CSS3, вы можете увеличить что-либо, просто убедитесь, что контейнер DIV установлен на переполнение: скрытый, чтобы остановить увеличенные края, выливающиеся из сторон.
Независимо от того, используете ли вы преобразования CSS3, или собственный масштаб и методы перевода холста, но проверьте приведенную выше ссылку на вычисления.
Update: Мех! Я просто разместить код здесь, а не заставить вас следовать ссылке:
$(document).ready(function()
{
var scale = 1; // scale of the image
var xLast = 0; // last x location on the screen
var yLast = 0; // last y location on the screen
var xImage = 0; // last x location on the image
var yImage = 0; // last y location on the image
// if mousewheel is moved
$("#mosaicContainer").mousewheel(function(e, delta)
{
// find current location on screen
var xScreen = e.pageX - $(this).offset().left;
var yScreen = e.pageY - $(this).offset().top;
// find current location on the image at the current scale
xImage = xImage + ((xScreen - xLast)/scale);
yImage = yImage + ((yScreen - yLast)/scale);
// determine the new scale
if (delta > 0)
{
scale *= 2;
}
else
{
scale /= 2;
}
scale = scale < 1 ? 1 : (scale > 64 ? 64 : scale);
// determine the location on the screen at the new scale
var xNew = (xScreen - xImage)/scale;
var yNew = (yScreen - yImage)/scale;
// save the current screen location
xLast = xScreen;
yLast = yScreen;
// redraw
$(this).find('div').css('-moz-transform', 'scale(' + scale + ')' + 'translate(' + xNew + 'px, ' + yNew + 'px' + ')')
.css('-moz-transform-origin', xImage + 'px ' + yImage + 'px')
return false;
});
});
Вы, конечно, нужно адаптировать его использовать масштаб холста и перевести методы.
Update 2: Просто заметил я, используя преобразование происхождения вместе с переводом. Мне удалось реализовать версию, которая просто использует масштаб и переводить самостоятельно, проверьте здесь http://www.dominicpettifer.co.uk/Files/Mosaic/MosaicTest.html Дождитесь загрузки изображений, затем используйте колесо мыши для увеличения, а также поддерживайте панорамирование, перемещая изображение вокруг. Он использует CSS3 Transforms, но вы можете использовать те же вычисления для своего Canvas.
Я, наконец, решил это, взял меня через 3 минуты после примерно двухнедельных занятий чем-то еще – csiz
Привет @SundayIronfoot, не могли бы вы снова положить свой код! Благодаря!! – chemitaxis
@Synday Ironfoot ссылка на его обновление не работает. Эта ссылка: http://www.dominicpettifer.co.uk/Files/Mosaic/MosaicTest.html Я хочу это имплицирование. Можете ли вы разместить здесь код? спасибо – Bogz
Я хочу разместить здесь некоторую информацию для тех, кто делает отдельный рисунок изображения и перемещает -измеряет его.
Это может быть полезно, если вы хотите сохранить масштабирование и положение окна просмотра.
Вот ящик:
function redraw_ctx(){
self.ctx.clearRect(0,0,canvas_width, canvas_height)
self.ctx.save()
self.ctx.scale(self.data.zoom, self.data.zoom) //
self.ctx.translate(self.data.position.left, self.data.position.top) // position second
// Here We draw useful scene My task - image:
self.ctx.drawImage(self.img ,0,0) // position 0,0 - we already prepared
self.ctx.restore(); // Restore!!!
}
Примечание Шкала ДОЛЖЕН быть первым.
А вот Zoomer:
function zoom(zf, px, py){
// zf - is a zoom factor, which in my case was one of (0.1, -0.1)
// px, py coordinates - is point within canvas
// eg. px = evt.clientX - canvas.offset().left
// py = evt.clientY - canvas.offset().top
var z = self.data.zoom;
var x = self.data.position.left;
var y = self.data.position.top;
var nz = z + zf; // getting new zoom
var K = (z*z + z*zf) // putting some magic
var nx = x - ((px*zf)/K);
var ny = y - ((py*zf)/K);
self.data.position.left = nx; // renew positions
self.data.position.top = ny;
self.data.zoom = nz; // ... and zoom
self.redraw_ctx(); // redraw context
}
и, конечно же, нам потребуется Dragger:
this.my_cont.mousemove(function(evt){
if (is_drag){
var cur_pos = {x: evt.clientX - off.left,
y: evt.clientY - off.top}
var diff = {x: cur_pos.x - old_pos.x,
y: cur_pos.y - old_pos.y}
self.data.position.left += (diff.x/self.data.zoom); // we want to move the point of cursor strictly
self.data.position.top += (diff.y/self.data.zoom);
old_pos = cur_pos;
self.redraw_ctx();
}
})
Я столкнулся с этой проблемой с помощью C++, который я, вероятно, не имел Я просто использовал макеты OpenGL, чтобы начать с ... в любом случае, если вы используете элемент управления, начало которого - верхний левый угол, и вы хотите, чтобы панорамирование/масштабирование, например, карты google, вот макет (используя allegro в качестве моего обработчика событий):
// initialize
double originx = 0; // or whatever its base offset is
double originy = 0; // or whatever its base offset is
double zoom = 1;
.
.
.
main(){
// ...set up your window with whatever
// tool you want, load resources, etc
.
.
.
while (running){
/* Pan */
/* Left button scrolls. */
if (mouse == 1) {
// get the translation (in window coordinates)
double scroll_x = event.mouse.dx; // (x2-x1)
double scroll_y = event.mouse.dy; // (y2-y1)
// Translate the origin of the element (in window coordinates)
originx += scroll_x;
originy += scroll_y;
}
/* Zoom */
/* Mouse wheel zooms */
if (event.mouse.dz!=0){
// Get the position of the mouse with respect to
// the origin of the map (or image or whatever).
// Let us call these the map coordinates
double mouse_x = event.mouse.x - originx;
double mouse_y = event.mouse.y - originy;
lastzoom = zoom;
// your zoom function
zoom += event.mouse.dz * 0.3 * zoom;
// Get the position of the mouse
// in map coordinates after scaling
double newx = mouse_x * (zoom/lastzoom);
double newy = mouse_y * (zoom/lastzoom);
// reverse the translation caused by scaling
originx += mouse_x - newx;
originy += mouse_y - newy;
}
}
}
.
.
.
draw(originx,originy,zoom){
// NOTE:The following is pseudocode
// the point is that this method applies so long as
// your object scales around its top-left corner
// when you multiply it by zoom without applying a translation.
// draw your object by first scaling...
object.width = object.width * zoom;
object.height = object.height * zoom;
// then translating...
object.X = originx;
object.Y = originy;
}
Вот альтернативный способ сделать это, который использует setTransform() вместо scale() и translate(). Все хранится в одном объекте. Предполагается, что холст находится на уровне 0,0 на странице, иначе вам нужно будет вычесть его позицию из кодовых страниц.
this.zoomIn = function (pageX, pageY) {
var zoomFactor = 1.1;
this.scale = this.scale * zoomFactor;
this.lastTranslation = {
x: pageX - (pageX - this.lastTranslation.x) * zoomFactor,
y: pageY - (pageY - this.lastTranslation.y) * zoomFactor
};
this.canvasContext.setTransform(this.scale, 0, 0, this.scale,
this.lastTranslation.x,
this.lastTranslation.y);
};
this.zoomOut = function (pageX, pageY) {
var zoomFactor = 1.1;
this.scale = this.scale/zoomFactor;
this.lastTranslation = {
x: pageX - (pageX - this.lastTranslation.x)/zoomFactor,
y: pageY - (pageY - this.lastTranslation.y)/zoomFactor
};
this.canvasContext.setTransform(this.scale, 0, 0, this.scale,
this.lastTranslation.x,
this.lastTranslation.y);
};
Сопровождающего кода для обработки панорамирования:
this.startPan = function (pageX, pageY) {
this.startTranslation = {
x: pageX - this.lastTranslation.x,
y: pageY - this.lastTranslation.y
};
};
this.continuePan = function (pageX, pageY) {
var newTranslation = {x: pageX - this.startTranslation.x,
y: pageY - this.startTranslation.y};
this.canvasContext.setTransform(this.scale, 0, 0, this.scale,
newTranslation.x, newTranslation.y);
};
this.endPan = function (pageX, pageY) {
this.lastTranslation = {
x: pageX - this.startTranslation.x,
y: pageY - this.startTranslation.y
};
};
Для получения ответа на вызов себе, считаю, что один и та же координата страницы должна соответствовать тем же холсту координаты до и после увеличения. Тогда вы можете сделать некоторые алгебры, начиная с этого уравнения:
(pageCoords - перевод)/Scale = canvasCoords
if(wheel > 0) {
this.scale *= 1.1;
this.offsetX -= (mouseX - this.offsetX) * (1.1 - 1);
this.offsetY -= (mouseY - this.offsetY) * (1.1 - 1);
}
else {
this.scale *= 1/1.1;
this.offsetX -= (mouseX - this.offsetX) * (1/1.1 - 1);
this.offsetY -= (mouseY - this.offsetY) * (1/1.1 - 1);
}
Лучшим решением будет просто переместить позицию окна просмотра на основе изменения масштаба , Точка масштабирования - это просто точка старого масштабирования и новый масштаб, который вы хотите оставить неизменным. То есть предварительный просмотр в окне просмотра и увеличенное изображение в окне просмотра имеют одинаковое значение по сравнению с видовым экраном. Учитывая, что мы масштабируемся относительно происхождения. Вы можете отрегулировать положение видового соответственно:
scalechange = newscale - oldscale;
offsetX = -(zoomPointX * scalechange);
offsetY = -(zoomPointY * scalechange);
Так на самом деле вы можете просто перемещаться над вниз и вправо при увеличении масштаба на коэффициент, сколько вы увеличены, по сравнению с точкой вы увеличенная на.
Это лучшее ответьте здесь. Жаль, что все более высокие ответы содержат огромное количество материалов, специфичных для библиотеки. – Jehan
Более ценным, чем вырезать и вставлять код, является объяснение того, что является лучшим решением и почему оно работает без багажа, особенно если оно длино в три строки. – Tatarize
scalechange = newscale/oldscale? –
Вам нужно, чтобы получить точку в мировом пространстве (в отличие от пространства экрана) до и после масштабирования, а затем перевести дельту.
mouse_world_position = to_world_position(mouse_screen_position);
zoom();
mouse_world_position_new = to_world_position(mouse_screen_position);
translation += mouse_world_position_new - mouse_world_position;
Позиция мыши находится в пространстве экрана, поэтому вы должны преобразовать ее в мировое пространство. Простой преобразующая должен быть похож на это:
world_position = screen_position/scale - translation
Вот мое решение для центрально-ориентированного изображения:
var MIN_SCALE = 1;
var MAX_SCALE = 5;
var scale = MIN_SCALE;
var offsetX = 0;
var offsetY = 0;
var $image = $('#myImage');
var $container = $('#container');
var areaWidth = $container.width();
var areaHeight = $container.height();
$container.on('wheel', function(event) {
event.preventDefault();
var clientX = event.originalEvent.pageX - $container.offset().left;
var clientY = event.originalEvent.pageY - $container.offset().top;
var nextScale = Math.min(MAX_SCALE, Math.max(MIN_SCALE, scale - event.originalEvent.deltaY/100));
var percentXInCurrentBox = clientX/areaWidth;
var percentYInCurrentBox = clientY/areaHeight;
var currentBoxWidth = areaWidth/scale;
var currentBoxHeight = areaHeight/scale;
var nextBoxWidth = areaWidth/nextScale;
var nextBoxHeight = areaHeight/nextScale;
var deltaX = (nextBoxWidth - currentBoxWidth) * (percentXInCurrentBox - 0.5);
var deltaY = (nextBoxHeight - currentBoxHeight) * (percentYInCurrentBox - 0.5);
var nextOffsetX = offsetX - deltaX;
var nextOffsetY = offsetY - deltaY;
$image.css({
transform : 'scale(' + nextScale + ')',
left : -1 * nextOffsetX * nextScale,
right : nextOffsetX * nextScale,
top : -1 * nextOffsetY * nextScale,
bottom : nextOffsetY * nextScale
});
offsetX = nextOffsetX;
offsetY = nextOffsetY;
scale = nextScale;
});
body {
background-color: orange;
}
#container {
margin: 30px;
width: 500px;
height: 500px;
background-color: white;
position: relative;
overflow: hidden;
}
img {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
max-width: 100%;
max-height: 100%;
margin: auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="container">
<img id="myImage" src="http://s18.postimg.org/eplac6dbd/mountain.jpg">
</div>
вы можете использовать scrollto (х, у) функция отрегулируйте положение полосы прокрутки вправо до точки, которую нужно отобразить после масштабирования. Для нахождения позиции мыши используйте event.clientX и event.clientY. this will help you
Вот реализация кода ответа @ tatarize с использованием PIXI.js. У меня есть окно просмотра, которое смотрит на часть очень большого изображения (например, стиль карт Google).
$canvasContainer.on('wheel', function (ev) {
var scaleDelta = 0.02;
var currentScale = imageContainer.scale.x;
var nextScale = currentScale + scaleDelta;
var offsetX = -(mousePosOnImage.x * scaleDelta);
var offsetY = -(mousePosOnImage.y * scaleDelta);
imageContainer.position.x += offsetX;
imageContainer.position.y += offsetY;
imageContainer.scale.set(nextScale);
renderer.render(stage);
});
$canvasContainer
мой HTML контейнер.imageContainer
- это мой контейнер PIXI, в котором есть изображение.mousePosOnImage
- это положение мыши относительно всего изображения (а не только порт представления).
Вот как я получил позицию мыши:
imageContainer.on('mousemove', _.bind(function(ev) {
mousePosOnImage = ev.data.getLocalPosition(imageContainer);
mousePosOnViewport.x = ev.data.originalEvent.offsetX;
mousePosOnViewport.y = ev.data.originalEvent.offsetY;
},self));
Не совсем прямой ответ на вопрос, но это то, что мне нужно. Спасибо. – newguy
мне нравится ответ Tatarize, но я предоставлю альтернативу. Это тривиальная проблема линейной алгебры, и предлагаемый мной метод хорошо работает с панорамированием, масштабированием, перекосом и т. Д. То есть он хорошо работает, если ваш образ уже преобразован.
При масштабировании матрицы шкала находится в точке (0, 0). Итак, если у вас есть изображение и масштабируйте его в 2 раза, нижняя правая точка будет удвоена как в направлениях x, так и в направлении y (используя соглашение, которое [0, 0] является левым верхним краем изображения).
Если вместо этого вы хотите увеличить изображение по центру, то решение будет следующим: (1) перевести изображение таким образом, чтобы его центр находился в (0, 0); (2) масштабировать изображение по х и у факторам; (3) перевести изображение назад. то есть
myMatrix
.translate(image.width/2, image.height/2) // 3
.scale(xFactor, yFactor) // 2
.translate(-image.width/2, -image.height/2); // 1
Более абстрактно, та же стратегия работает для любой точки. Если, например, вы хотите, чтобы масштабировать изображение в точке Р:
myMatrix
.translate(P.x, P.y)
.scale(xFactor, yFactor)
.translate(-P.x, -P.y);
И, наконец, если изображение уже преобразуется каким-либо образом (например, если он вращается, искажено, переводиться или масштабируется) , то текущая трансформация должна быть сохранена. В частности, преобразование, определенное выше, должно быть после умножено (или умножено справа) на текущее преобразование.
myMatrix
.translate(P.x, P.y)
.scale(xFactor, yFactor)
.translate(-P.x, -P.y)
.multiply(myMatrix);
У вас есть это. Вот план, который показывает это в действии. Прокрутите колесико мыши по точкам, и вы увидите, что они постоянно остаются на месте. (Протестировано только в Chrome.) http://plnkr.co/edit/3aqsWHPLlSXJ9JCcJzgH?p=preview
- 1. Увеличение и уменьшение масштаба
- 2. Увеличение и уменьшение масштаба в Matlab
- 3. Увеличение масштаба диаграммы линий
- 4. Увеличение и уменьшение масштаба сцены?
- 5. Увеличение и уменьшение масштаба PictureBox C#
- 6. Увеличение и уменьшение масштаба: холст смещение
- 7. Увеличение масштаба изображения и улучшение анимации
- 8. Android. Масштаб холста и перевод
- 9. IOS перевод и масштаб анимации
- 10. D3 масштаб преобразования и перевод
- 11. Как UIImage решает масштаб масштаба?
- 12. Увеличение и уменьшение масштаба изображения в Xamarin.Forms
- 13. QGraphicsView Увеличение и уменьшение масштаба с QPushButton
- 14. Увеличение и уменьшение масштаба в AS3
- 15. увеличение и уменьшение масштаба признакам в ИОС
- 16. Добавление прокрутки и увеличение его масштаба
- 17. Увеличение и уменьшение масштаба в Direct3D
- 18. Увеличение масштаба изображения на карте
- 19. Увеличение и уменьшение масштаб Переход в Codenameone
- 20. UNITY- Камера Увеличение и уменьшение масштаба с перемещением символов
- 21. Увеличение и уменьшение масштаба в mapKit с помощью слайдера
- 22. SVG Линейного градиент Масштаб и перевод Вопрос
- 23. JQuery Увеличение и уменьшение масштаба работы с рывками вместо гладких
- 24. , используя масштаб и преобразование вместе для объекта
- 25. Как получить масштаб, поворот и перевод после отслеживания функций?
- 26. Как сохранить масштаб масштаба на PinchGestureRecognizer?
- 27. Используйте масштаб шкалы d3 вместо линейного масштаба
- 28. Увеличение и уменьшение масштаба сетки и изображения в swing
- 29. Увеличение масштаба графика в случайной точке с помощью d3js
- 30. Увеличение масштаба изображения, показывающее более подробную информацию
Я использовал это для масштабирования моего холста, и он отлично работает! Единственное, что я должен добавить, это то, что вычисление суммы масштабирования не так, как вы ожидали. "var zoom = 1 + wheel/2;" т. е. это приводит к 1,5 для масштабирования и 0,5 для масштабирования. Я отредактировал это в своей версии, чтобы у меня было 1,5 для увеличения и 1/1.5 для масштабирования, что делает масштаб масштабирования и масштабирования равным. Поэтому, если вы увеличиваете масштаб и увеличиваете масштаб изображения, у вас будет то же изображение, что и до масштабирования. – Chris
Святые шники! Это действительно работает! – sneilan
Обратите внимание, что это не работает в Firefox, но этот метод можно легко применить к [jQuery mousewheel plugin] (http://plugins.jquery.com/project/mousewheel). Спасибо, что поделился! – johndodo