gpt4 book ai didi

javascript - 在 HTML5 中创建可拖动和可缩放的网格

转载 作者:行者123 更新时间:2023-11-30 09:18:30 27 4
gpt4 key购买 nike

与其他 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(抱歉,积分还不够)

随着图形向右移动,从 Canvas 尺寸中隐藏的线被移回开头,反之亦然。我不太确定如何使用鼠标移动网格以及缩放。与 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>

此方法不允许我向左或向上滚动。它还会加快一段距离后的拖动速度,并且不能很好地处理负运动。

最佳答案

平移和缩放

我没有太多的时间,所以代码将不得不做大部分的解释。

下面的示例使用鼠标和鼠标滚轮进行平移和缩放。

对象 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),如果您想清除 Canvas ,则需要这样做。

片段更新 2021

我已经添加了

  • drawGrid(gridScreenSize, adaptive) 的参数

    • gridScreenSize 屏幕像素中的网格大小(自适应模式)。在世界像素中(静态模式)

    • adaptive 如果 true 网格比例适应世界比例。 false 网格大小是固定的。当 false 常量 gridLimit 设置要渲染的最大行数

  • scaleRate 控制缩放率的常量。查看评论了解更多。

  • 显示文本以显示当前比例。

  • 用于在静态和自适应网格大小之间切换的复选框。

const ctx = canvas.getContext("2d");
requestAnimationFrame(update);
const mouse = {x : 0, y : 0, button : false, wheel : 0, lastX : 0, lastY : 0, drag : false};
const gridLimit = 64; // max grid lines for static grid
const gridSize = 128; // grid size in screen pixels for adaptive and world pixels for static
const scaleRate = 1.02; // Closer to 1 slower rate of change
// Less than 1 inverts scaling change and same rule
// Closer to 1 slower rate of change
const topLeft = {x: 0, y: 0}; // holds top left of canvas in world coords.


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"].forEach(name => document.addEventListener(name,mouseEvents));
document.addEventListener("wheel",mouseEvents, {passive: false});

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;
},
toWorld(x, y, point = {}) { // converts from screen coords to world coords
const inv = 1 / this.scale;
point.x = (x - this.x) * inv;
point.y = (y - this.y) * inv;
return point;
},
}
function drawGrid(gridScreenSize = 128, adaptive = true){
var scale, gridScale, size, x, y, limitedGrid = false;
if (adaptive) {
scale = 1 / panZoom.scale;
gridScale = 2 ** (Math.log2(gridScreenSize * scale) | 0);
size = Math.max(w, h) * scale + gridScale * 2;
x = ((-panZoom.x * scale - gridScale) / gridScale | 0) * gridScale;
y = ((-panZoom.y * scale - gridScale) / gridScale | 0) * gridScale;
} else {
gridScale = gridScreenSize;
size = Math.max(w, h) / panZoom.scale + gridScale * 2;
panZoom.toWorld(0,0, topLeft);
x = Math.floor(topLeft.x / gridScale) * gridScale;
y = Math.floor(topLeft.y / gridScale) * gridScale;
if (size / gridScale > gridLimit) {
size = gridScale * gridLimit;
limitedGrid = true;
}
}
panZoom.apply();
ctx.lineWidth = 1;
ctx.strokeStyle = "#000";
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();

info.textContent = "Scale: 1px = " + (1/panZoom.scale).toFixed(4) + " world px ";
limitedGrid && (info.textContent += " Static grid limit " + (gridLimit * gridLimit) + " cells");
}
function drawPoint(x, y) {
const worldCoord = panZoom.toWorld(x, y);
panZoom.apply();
ctx.lineWidth = 1;
ctx.strokeStyle = "red";
ctx.beginPath();
ctx.moveTo(worldCoord.x - 10, worldCoord.y);
ctx.lineTo(worldCoord.x + 10, worldCoord.y);
ctx.moveTo(worldCoord.x, worldCoord.y - 10);
ctx.lineTo(worldCoord.x, worldCoord.y + 10);
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 / scaleRate : scaleRate;
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(gridSize, adaptiveGridCb.checked);
drawPoint(mouse.x, mouse.y);
requestAnimationFrame(update);
}
canvas { position : absolute; top : 0px; left : 0px; }
div {
position : absolute;
top : 5px;
left : 5px;
font-family: arial;
font-size: 16px;
background: #FFFD;
}
<canvas id="canvas"></canvas>
<div>
<label for="adaptiveGridCb">Adaptive grid</label>
<input id="adaptiveGridCb" type="checkbox" checked/>
<span id="info"></span>
</div>

关于javascript - 在 HTML5 中创建可拖动和可缩放的网格,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53310138/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com