我目前正在努力在我的 Vue 应用程序中向画布添加平移系统。虽然我已经成功实现了平移系统,但我面临着高效渲染无限点网格的挑战。
这是我的组件的代码:
<template>
<div>
<div>
<canvas
@mousedown="baseEditorMouseDown"
@mouseup="baseEditorMouseUp"
@mousemove="baseEditorMouseMove"
ref="baseEditor"
></canvas>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, type Ref, computed, onMounted } from "vue";
const baseEditor: Ref<HTMLCanvasElement | null> = ref(null);
const context: Ref<CanvasRenderingContext2D | null> = ref(null);
const gridOffset = ref(50);
const imagePosition: Ref<any> = ref({
x: 0,
y: 0,
});
const canvasBaseImage = ref(new Image());
const isMasking = ref(false);
const lastMaskPosition: Ref<any> = ref({
x: 0,
y: 0,
});
const isPanning = ref(false);
const lastClickPosition = ref({ x: 0, y: 0 });
const lastOffsetTemp = ref({ x: 0, y: 0 });
const lastOffset = ref({ x: 0, y: 0 });
const computedGridCircles = computed(() => {
var coords = [];
for (
let x = -window.innerWidth + gridOffset.value / 2;
x < window.innerWidth * 2;
x += gridOffset.value
) {
for (
let y = -window.innerHeight + gridOffset.value / 2;
y < window.innerHeight * 2;
y += gridOffset.value
) {
coords.push({
x,
y,
});
}
}
return coords;
});
function drawGridCircles() {
if (context.value) {
context.value.beginPath();
for (let i = 0; i < computedGridCircles.value.length; i++) {
const coord = computedGridCircles.value[i];
context.value.rect(
coord.x + lastOffsetTemp.value.x + lastOffset.value.x,
coord.y + lastOffsetTemp.value.y + lastOffset.value.y,
3,
3
);
}
context.value.fillStyle = "#333";
context.value.fill();
}
}
function baseEditorMouseMove(event: MouseEvent) {
if (isPanning.value) {
lastOffset.value.x = event.clientX - lastClickPosition.value.x;
lastOffset.value.y = event.clientY - lastClickPosition.value.y;
updatePanning();
}
}
function baseEditorMouseDown(event: MouseEvent) {
isPanning.value = true;
lastClickPosition.value.x = event.clientX;
lastClickPosition.value.y = event.clientY;
}
function baseEditorMouseUp(event: MouseEvent) {
isMasking.value = false;
isPanning.value = false;
lastOffsetTemp.value.x += lastOffset.value.x;
lastOffsetTemp.value.y += lastOffset.value.y;
}
function clearCanvas() {
if (context.value) {
context.value.clearRect(0, 0, window.innerWidth, window.innerHeight);
}
}
function updatePanning() {
clearCanvas();
drawGridCircles();
}
onMounted(() => {
if (baseEditor.value) {
context.value = baseEditor.value.getContext("2d");
baseEditor.value.width = window.innerWidth;
baseEditor.value.height = window.innerHeight;
}
drawGridCircles();
});
</script>
在compatedGridCircles函数中,我根据用户的窗口大小计算网格点的坐标。然而,这并没有提供无限的网格。我参考了一些资源,包括:
但是我在数学计算中遗漏了一些东西。
这里有一个 StackBlitz 演示,以便更好地理解。 https://stackblitz.com/edit/vue3-vite-typescript-starter-21gys6?file=src%2FApp.vue
我感谢任何有关通过高效渲染实现无限网格的指导。谢谢您的帮助!
我相信我已经找到了解决方案。
首先,用户无法抓取超出其屏幕分辨率允许的内容。因此,我推断起始坐标和终止坐标之间的差异应该超过屏幕分辨率。
例如,我有一个 1920x1080 的显示器,因此开始和停止 X 位置之间的差异应大于 1920,才能呈现无限滚动的外观。为了确保这一点,我还添加了五倍网格大小的差值。同样的原理也适用于垂直位置。
我绘制与可见区域相当的矩形,以防止抓取太多后出现滞后或冻结问题。
这就是我修改我的
drawGridCircles
功能的方式:
function drawGridCircles() {
if (context.value) {
context.value.beginPath();
const startX = Math.floor(-currentOffsetPositions.value.x / gridSize.value) * gridSize.value + gridSize.value / 2;
const startY = Math.floor(-currentOffsetPositions.value.y / gridSize.value) * gridSize.value + gridSize.value / 2;
const finishX = startX + window.innerWidth + gridSize.value * 5
const finishY = startY + window.innerHeight + gridSize.value * 5
for (let x = startX; x < finishX; x += gridSize.value) {
for (let y = startY; y < finishY; y += gridSize.value) {
context.value.rect(x + currentOffsetPositions.value.x, y + currentOffsetPositions.value.y, 3, 3)
}
}
context.value.fillStyle = "#333";
context.value.fill();
}
}
完整代码可以在这里找到: https://stackblitz.com/edit/vue3-vite-typescript-starter-qf1cey?file=src%2FApp.vue