[英]Zoom Canvas to Mouse Cursor
我正在编写一个 HTML5 <canvas > 项目,该项目涉及使用滚轮放大和缩小图像。 我想像谷歌地图一样放大光标,但我完全不知道如何计算运动。
我所拥有的:图像 x 和 y(左上角); 图像宽度和高度; 相对于画布中心的光标 x 和 y。
简而言之,您希望通过偏移量来translate()
画布上下文,使用scale()
来放大或缩小,然后translate()
回鼠标偏移的反面。 请注意,您需要将光标位置从屏幕空间转换为已转换的画布上下文。
ctx.translate(pt.x,pt.y);
ctx.scale(factor,factor);
ctx.translate(-pt.x,-pt.y);
我在我的网站上提供了一个完整的工作示例 ,供您检查,支持拖动,单击放大,按住Shift键单击,或向上/向下滚动滚轮。
我希望,这些JS库可以帮助你:(HTML5,JS)
http://www.netzgesta.de/loupe/
https://github.com/akademy/CanvasZoom
https://github.com/zynga/scroller
至于我,我正在使用放大镜。 这很棒! 对你来说最好的情况 - 卷轴。
我最近需要存档与Phrogz已经完成的相同的结果,但不是使用context.scale()
,而是根据比率计算每个对象的大小。
这就是我提出的。 它背后的逻辑非常简单。 在缩放之前,我以百分比计算边缘的点距离,然后将视口调整到正确的位置。
我花了很长时间来提出它,希望它节省了一些人的时间。
$(function () { var canvas = $('canvas.main').get(0) var canvasContext = canvas.getContext('2d') var ratio = 1 var vpx = 0 var vpy = 0 var vpw = window.innerWidth var vph = window.innerHeight var orig_width = 4000 var orig_height = 4000 var width = 4000 var height = 4000 $(window).on('resize', function () { $(canvas).prop({ width: window.innerWidth, height: window.innerHeight, }) }).trigger('resize') $(canvas).on('wheel', function (ev) { ev.preventDefault() // for stackoverflow var step if (ev.originalEvent.wheelDelta) { step = (ev.originalEvent.wheelDelta > 0) ? 0.05 : -0.05 } if (ev.originalEvent.deltaY) { step = (ev.originalEvent.deltaY > 0) ? 0.05 : -0.05 } if (!step) return false // yea.. var new_ratio = ratio + step var min_ratio = Math.max(vpw / orig_width, vph / orig_height) var max_ratio = 3.0 if (new_ratio < min_ratio) { new_ratio = min_ratio } if (new_ratio > max_ratio) { new_ratio = max_ratio } // zoom center point var targetX = ev.originalEvent.clientX || (vpw / 2) var targetY = ev.originalEvent.clientY || (vph / 2) // percentages from side var pX = ((vpx * -1) + targetX) * 100 / width var pY = ((vpy * -1) + targetY) * 100 / height // update ratio and dimentsions ratio = new_ratio width = orig_width * new_ratio height = orig_height * new_ratio // translate view back to center point var x = ((width * pX / 100) - targetX) var y = ((height * pY / 100) - targetY) // don't let viewport go over edges if (x < 0) { x = 0 } if (x + vpw > width) { x = width - vpw } if (y < 0) { y = 0 } if (y + vph > height) { y = height - vph } vpx = x * -1 vpy = y * -1 }) var is_down, is_drag, last_drag $(canvas).on('mousedown', function (ev) { is_down = true is_drag = false last_drag = { x: ev.clientX, y: ev.clientY } }) $(canvas).on('mousemove', function (ev) { is_drag = true if (is_down) { var x = vpx - (last_drag.x - ev.clientX) var y = vpy - (last_drag.y - ev.clientY) if (x <= 0 && vpw < x + width) { vpx = x } if (y <= 0 && vph < y + height) { vpy = y } last_drag = { x: ev.clientX, y: ev.clientY } } }) $(canvas).on('mouseup', function (ev) { is_down = false last_drag = null var was_click = !is_drag is_drag = false if (was_click) { } }) $(canvas).css({ position: 'absolute', top: 0, left: 0 }).appendTo(document.body) function animate () { window.requestAnimationFrame(animate) canvasContext.clearRect(0, 0, canvas.width, canvas.height) canvasContext.lineWidth = 1 canvasContext.strokeStyle = '#ccc' var step = 100 * ratio for (var x = vpx; x < width + vpx; x += step) { canvasContext.beginPath() canvasContext.moveTo(x, vpy) canvasContext.lineTo(x, vpy + height) canvasContext.stroke() } for (var y = vpy; y < height + vpy; y += step) { canvasContext.beginPath() canvasContext.moveTo(vpx, y) canvasContext.lineTo(vpx + width, y) canvasContext.stroke() } canvasContext.strokeRect(vpx, vpy, width, height) canvasContext.beginPath() canvasContext.moveTo(vpx, vpy) canvasContext.lineTo(vpx + width, vpy + height) canvasContext.stroke() canvasContext.beginPath() canvasContext.moveTo(vpx + width, vpy) canvasContext.lineTo(vpx, vpy + height) canvasContext.stroke() canvasContext.restore() } animate() })
<!DOCTYPE html> <html> <head> <title></title> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> </head> <body> <canvas class="main"></canvas> </body> </html>
我以@ Phrogz的答案为基础,制作了一个小型库,可以通过拖动,缩放和旋转来实现画布。 这是一个例子。
var canvas = document.getElementById('canvas')
//assuming that @param draw is a function where you do your main drawing.
var control = new CanvasManipulation(canvas, draw)
control.init()
control.layout()
//now you can drag, zoom and rotate in canvas
您可以在项目页面上找到更详细的示例和文档
与多个矩阵调用ctx.translate
、 ctx.scale
、 ctx.translate
ctx.translate
,使用ctx.setTransform
更高的性能。
不需要复杂的转换反转,因为昂贵的 DOM 矩阵调用 tp 在缩放和屏幕坐标系之间转换点。
灵活性,因为如果您使用不同的转换渲染内容,则不需要使用ctx.save
和ctx.restore
。 使用ctx.setTransform
而不是潜在的帧率ctx.restore
调用返回到转换
易于反转变换并获得(屏幕)像素位置的世界坐标,反之亦然。
使用鼠标和鼠标滚轮在鼠标位置放大和缩小
使用此方法通过 CSS 转换在答案底部的 CSS 演示点(鼠标)缩放页面内容的示例也有下一个示例中演示的副本。
以及使用 setTransform 在某个点缩放画布内容的此方法的示例
给定比例和像素位置,您可以按如下方式获得新比例...
const origin = {x:0, y:0}; // canvas origin
var scale = 1; // current scale
function scaleAt(x, y, scaleBy) { // at pixel coords x, y scale by scaleBy
scale *= scaleBy;
origin.x = x - (x - origin.x) * scaleBy;
origin.y = y - (y - origin.y) * scaleBy;
}
定位画布并绘制内容
ctx.setTransform(scale, 0, 0, scale, origin.x, origin.y);
ctx.drawImage(img, 0, 0);
如果您有鼠标坐标,则使用
const zoomBy = 1.1; // zoom in amount
scaleAt(mouse.x, mouse.y, zoomBy); // will zoom in at mouse x, y
scaleAt(mouse.x, mouse.y, 1 / zoomBy); // will zoom out by same amount at mouse x,y
恢复默认转换
ctx.setTransform(1,0,0,1,0,0);
获取缩放坐标系中某个点的坐标和缩放坐标系中某个点的屏幕位置
屏幕到世界
function toWorld(x, y) { // convert to world coordinates
x = (x - origin.x) / scale;
y = (y - origin.y) / scale;
return {x, y};
}
屏幕上的世界
function toScreen(x, y) {
x = x * scale + origin.x;
y = y * scale + origin.y;
return {x, y};
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.