与其他HTML5 如何创建网格问题不同,我想知道如何使一个可拖动和可伸缩。
绘制网格非常简单:
var c = document.getElementById('canvas');
var ctx = c.getContext('2d');
var width = window.innerWidth;
var height = window.innerHeight;
c.width = width;
c.height = height;
drawGrid(width, height, 40);
function drawGrid(gridWidth, gridHeight, boxSize) {
ctx.clearRect(0, 0, c.width, c.height);
ctx.beginPath();
for (var i = 0; i <= gridWidth; i += boxSize) {
ctx.moveTo(i, 0);
ctx.lineTo(i, gridHeight);
}
for (var i = 0; i <= gridHeight; i += boxSize) {
ctx.moveTo(0, i);
ctx.lineTo(gridWidth, i);
}
ctx.strokeStyle = "rgba( 210, 210, 210, 1 )";
ctx.stroke();
}
html,
body {
overflow: hidden;
}
#canvas {
position: absolute;
top: 0;
left: 0;
}
<canvas id="canvas"></canvas>
现在要使其可拖动,有很多方法可以实现,但我专注于创建类似于以下内容的无限移动网格的错觉:
Example Image (对不起,还没有足够的学分)
当图形向右移动时,画布大小中隐藏的线将移回到开头,反之亦然。我不太确定如何使用鼠标移动网格以及缩放比例。与SVG不同,缩放时线条趋于模糊。 创建无限移动和缩放网格的最快方法是什么?
编辑: 我采用了类似的方法来移动网格,并使用图像模式填充屏幕。
var c = document.getElementById("canvas"),
ctx = c.getContext("2d");
var width = window.innerWidth;
var height = window.innerHeight;
var itemIsSelected = false;
var clicked = function(e) {
var x = e.pageX;
var y = e.pageY;
}
draw(width, height);
draggable('#app');
function draw(width, height) {
c.width = width;
c.height = height;
generateBackground();
}
function draggable(item) {
var isMouseDown = false;
document.onmousedown = function(e) {
e.preventDefault();
clicked.x = e.pageX;
clicked.y = e.pageY;
$(item).css('cursor', 'all-scroll');
isMouseDown = true;
};
document.onmouseup = function(e) {
e.preventDefault();
isMouseDown = false;
$(item).css('cursor', 'default');
};
document.onmousemove = function(e) {
e.preventDefault();
if (isMouseDown == true) {
var mouseX = e.pageX;
var mouseY = e.pageY;
generateBackground(mouseX, mouseY, clicked.x, clicked.y);
}
};
}
function generateBackground(x, y, initX, initY) {
distanceX = x - initX;
distanceY = y - initY;
ctx.clearRect(0, 0, c.width, c.height);
var bgImage = document.getElementById("bg")
var pattern = ctx.createPattern(bgImage, "repeat");
ctx.rect(0, 0, width, height);
ctx.fillStyle = pattern;
ctx.fill();
ctx.translate(Math.sqrt(distanceX), Math.sqrt(distanceY));
}
html,
body {
overflow: hidden;
}
#canvas {
top: 0;
left: 0;
position: absolute;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas"></canvas>
<img src="https://i.imgur.com/2MupHjw.png" id="bg" hidden>
此方法不允许我向左或向上滚动。它也可以加速一定距离后的拖动,并且不能很好地处理负向运动。
答案 0 :(得分:4)
我没有太多的常规,所以代码将不得不做大部分解释。
下面的示例使用鼠标和鼠标滚轮进行平移和缩放。
对象panZoom
保存有关缩放(scale
)和平移位置(x
,y
)的信息
要平移,只需使用鼠标位置的更改来更改panZoom
x
,y
的位置。您无需缩放鼠标移动,因为它们不会受到缩放的影响。
通过功能panZoom.scaleAt(x,y,scale)
进行缩放,其中x
,y
是鼠标位置,scaleBy是缩放比例的量。例如,panZoom.scaleAt(100,100, 2)
将在位置100,100放大2倍,panZoom.scaleAt(100,100, 1/2)
将在相同位置将图像放大2倍。有关更多详细信息,请参见update
函数。
要绘制panZoom
坐标系,您需要调用函数panZoom.apply
,该函数设置上下文转换以匹配panZoom的设置。函数drawGrid
是一个示例。它会绘制一个网格以适合当前的平移和缩放。
请注意,要恢复正常的屏幕坐标,只需调用ctx.setTransform(1,0,0,1,0,0)
,如果要清除画布,则需要这样做。
const ctx = canvas.getContext("2d");
requestAnimationFrame(update);
const mouse = {x : 0, y : 0, button : false, wheel : 0, lastX : 0, lastY : 0, drag : false};
function mouseEvents(e){
const bounds = canvas.getBoundingClientRect();
mouse.x = e.pageX - bounds.left - scrollX;
mouse.y = e.pageY - bounds.top - scrollY;
mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
if(e.type === "wheel"){
mouse.wheel += -e.deltaY;
e.preventDefault();
}
}
["mousedown", "mouseup", "mousemove", "wheel"].forEach(name => document.addEventListener(name,mouseEvents));
const panZoom = {
x : 0,
y : 0,
scale : 1,
apply() { ctx.setTransform(this.scale, 0, 0, this.scale, this.x, this.y) },
scaleAt(x, y, sc) { // x & y are screen coords, not world
this.scale *= sc;
this.x = x - (x - this.x) * sc;
this.y = y - (y - this.y) * sc;
},
}
function drawGrid(){
const scale = 1 / panZoom.scale;
var gridScale = 2 ** (Math.log2(128 * scale) | 0);
var size = Math.max(w, h) * scale + gridScale * 2;
var x = ((-panZoom.x * scale - gridScale) / gridScale | 0) * gridScale;
var y = ((-panZoom.y * scale - gridScale) / gridScale | 0) * gridScale;
panZoom.apply();
ctx.lineWidth = 1;
ctx.strokeStyle = "black";
ctx.beginPath();
for (i = 0; i < size; i += gridScale) {
ctx.moveTo(x + i, y);
ctx.lineTo(x + i, y + size);
ctx.moveTo(x, y + i);
ctx.lineTo(x + size, y + i);
}
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset the transform so the lineWidth is 1
ctx.stroke();
}
var w = canvas.width;
var h = canvas.height;
function update(){
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
ctx.globalAlpha = 1; // reset alpha
if (w !== innerWidth || h !== innerHeight) {
w = canvas.width = innerWidth;
h = canvas.height = innerHeight;
} else {
ctx.clearRect(0, 0, w, h);
}
if (mouse.wheel !== 0) {
let scale = 1;
scale = mouse.wheel < 0 ? 1 / 1.01 : 1.01;
mouse.wheel *= 0.8;
if(Math.abs(mouse.wheel) < 1){
mouse.wheel = 0;
}
panZoom.scaleAt(mouse.x, mouse.y, scale); //scale is the change in scale
}
if(mouse.button){
if(!mouse.drag){
mouse.lastX = mouse.x;
mouse.lastY = mouse.y;
mouse.drag = true;
} else {
panZoom.x += mouse.x - mouse.lastX;
panZoom.y += mouse.y - mouse.lastY;
mouse.lastX = mouse.x;
mouse.lastY = mouse.y;
}
}else if(mouse.drag){
mouse.drag = false;
}
drawGrid();
requestAnimationFrame(update);
}
canvas { position : absolute; top : 0px; left : 0px; }
<canvas id="canvas"></canvas>