- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
与其他 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 ,则需要这样做。
我已经添加了
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/
我正在使用 gridstack 来显示我的小部件。拖动和调整大小适用于 gridstack 卡,但当我将卡拖到底部时,卡的容器不会滚动。我想在拖动卡片时滚动那个容器。该容器只是一个 div 元素,所有
我目前正在构建一个多个处理的 slider 小部件,并且目前正在实现手势检测器。我有一个问题,如果您用第二根手指触摸/拖动屏幕,检测器会识别它并调用 onDragUpdate 函数,这是我试图禁用的功
我是 AngularJS 的新手,我对当前的项目有点压力,这就是我在这里问的原因。 我看到 AngularJS 有 ng-dragstart 事件( http://docs.ng.dart.ant.c
关于缩放大图像和平移有什么建议吗?最好内联在页面上。 我一直在使用PanoJS(又名GSV2),但是现在越来越多的人使用iPhone/iPad/Android类型的设备,这个库要么太慢,要么旧版本不支
我在尝试拖动 JPanel 时遇到问题。如果我纯粹在 MouseDragged 中将其实现为: public void mouseDragged(MouseEvent me) { me.getS
我有一个需要与屏幕底部对齐的 ImageButton,当我单击并拖动它时,它会随着我的手指移动一定距离,超过该距离它不会远离原点,而是继续跟随我的手指从原点开始的方向。 当我松手时,按钮必须滑回原点。
关闭。这个问题需要更多focused .它目前不接受答案。 想改进这个问题吗? 更新问题,使其只关注一个问题 editing this post . 关闭 9 年前。 Improve this qu
我需要一个解决方案来实现拖动和缩放,在不使用矩阵的情况下围绕屏幕 onTouch 事件路由 ImageView。我搜索了很多但所有答案都是用矩阵完成的,因为我在屏幕矩阵中有多个对象不适合我,我想拖动和
我需要一些帮助来拖动 UIView 以显示主视图下方的菜单 View 。 我有两个 UIView。menuView - 包括菜单按钮和标签 和 mainView - 位于 menuView 之上。 我
谷歌地图即使设为 true 也不可拖动/拖动。 重现步骤:在新选项卡中加载带有 map 的页面,检查并在不执行任何操作的情况下转到移动 View 。现在在移动 View 中左右上下拖动 map ,它将
我在绘制和缩放 ImageView 时遇到问题。请帮帮我.. 当我画一些东西然后拖动或缩放图像时 - 绘图保留在原处,如您在屏幕截图中所见。而且我只需要简单地在图片上绘图,并且可以缩放和拖动这张图片。
所以我试图模拟鼠标左键单击和鼠标左键释放来执行一些自动拖放操作。 它目前在 C# Winforms(是的,winforms :|)中并且有点笨拙。 基本上,一旦发送了点击,我希望它根据 Kinect
我有一个巨大的 HTML5 Canvas,我希望它像谷歌地图一样工作:用户可以拖动它并始终只看到它的一小部分(屏幕大小)。它只呈现您在屏幕上可以看到的部分。我该怎么做?你有想法吗? 最佳答案 2 个简
我有以下拖放应用程序代码,它在桌面上按预期工作,但是当我想在移动设备上使用该应用程序时,拖动事件无法按预期工作。我知道触摸事件是必需的,但我不确定如何设置它们和实现这些功能。 .object
我正在使用 react-leaflet map ,我用状态改变了很多 map 属性,但是当使用状态来改变拖动属性时它不起作用。 { this.map = map}}
我知道我可以轻松地允许用户在OpenLayers中选择多个功能/几何图形,但是随后我希望使用户能够轻松地同时拖动/移动所有选定的功能。 使用ModifyFeature控件一次只能移动一个要素...是否
我有一个 Windows Phone 运行时应用程序,我使用 xaml 在 map 上显示图钉。 当我拖动 map 时,控件试图保持在相同位置时会出现一些滞后。 任何帮助,将不胜感
我通常不会提出此类问题/答案,但我想我会这样做,因为我已经看到这个问题被问了 20 多次,但没有一个答案真正有效。简而言之,问题是,如果您在可拖动 jQuery 项目内的任何位置有可滚动内容(over
我确信一定有一种简单的方法可以做到这一点,但到目前为止,我已经花了很长时间在各种兔子洞中没有成功。 我有一个支持拖放的 Collection View 。被拖动的单元格有 UIImageView在 c
如何在两个virtualtreeview之间复制以复制所有列,而不仅仅是第一列? 复制前: 复制后: 最佳答案 树控件不保存任何数据。它不包含要显示的列数据,因此无法复制它。而是,当树控件想要显示任何
我是一名优秀的程序员,十分优秀!