【发布时间】:2011-07-08 13:54:05
【问题描述】:
我正在编写一个 HTML5
我所拥有的:图像 x 和 y(左上角);图像宽度和高度;光标 x 和 y 相对于画布的中心。
【问题讨论】:
-
你应该接受这个答案或修改你的问题
标签: javascript html canvas zooming scrollwheel
我正在编写一个 HTML5
我所拥有的:图像 x 和 y(左上角);图像宽度和高度;光标 x 和 y 相对于画布的中心。
【问题讨论】:
标签: javascript html canvas zooming scrollwheel
简而言之,您希望translate() 画布上下文通过您的偏移量,scale() 放大或缩小,然后translate() 回到鼠标偏移量的反面。请注意,您需要将光标位置从屏幕空间转换为转换后的画布上下文。
ctx.translate(pt.x,pt.y);
ctx.scale(factor,factor);
ctx.translate(-pt.x,-pt.y);
我在我的网站上放了一个full working example供你查看,支持拖动、点击放大、shift-点击缩小、滚轮上/下。
唯一的(当前)问题是 Safari zooms too fast 与 Chrome 或 Firefox 相比。
【讨论】:
我希望,这些 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
您可以在项目的page 上找到更详细的示例和文档
【讨论】:
使用ctx.setTransform 比多个矩阵调用ctx.translate、ctx.scale、ctx.translate 提供更高的性能。
不需要复杂的转换反转,因为昂贵的 DOM 矩阵调用 tp 在缩放坐标系和屏幕坐标系之间转换点。
灵活性,因为如果您使用不同的转换来渲染内容,则无需使用ctx.save 和ctx.restore。使用ctx.setTransform 回到转换,而不是使用潜在的帧速率来破坏ctx.restorecall
很容易反转变换并获得(屏幕)像素位置的世界坐标,反之亦然。
使用鼠标和鼠标滚轮在鼠标位置放大和缩小
答案底部使用scale page content at a point (mouse) via CSS transform CSS Demo 的示例也有来自下一个示例的演示副本。
还有这个方法的例子用scale canvas content at a point using 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};
}
【讨论】: