@scory02/grid-view
v1.0.1
Published
一个高性能的 Vue 无限生长网格视图组件,可用于可视化编辑器、画布等场景。
Readme
@scory02/grid-view
一个高性能的 Vue 无限生长网格视图组件,可用于可视化编辑器、画布等场景。
安装
npm install @scory02/grid-view使用方法
<template>
<GridView :gridType="'lines'" :zoomLevel="100" />
</template>
<script>
import GridView from '@scory02/grid-view/gridView.vue'
export default {
components: { GridView }
}
</script>完整使用代码
<template>
<div>
<div
class="grid-root"
@wheel="onWheel"
@mousedown="onMouseDown"
@mousemove="onMouseMove"
@mouseup="onMouseUp"
@mouseleave="onMouseLeave">
<gridView v-bind="paneOptions" />
</div>
<div ref="paneContainer" class="pane-container"></div>
</div>
</template>
<script>
import gridView from '@scory02/grid-view/gridView.vue';
import { Pane } from "tweakpane";
export default {
components: { gridView },
data() {
return {
paneOptions: {
gridType: 'lines',
zoomLevel: 100,
gridSize: 100,
max_size: 2,
viewX: 0,
viewY: 0,
gridColor: '#000000'
},
isDragging: false,
lastX: 0,
lastY: 0,
min_zoom: 1,
max_zoom: 1000,
pane: null
};
},
mounted() {
this.pane = new Pane({
title: 'Grid Controls',
container: this.$refs.paneContainer
});
this.pane.addBinding(this.paneOptions, 'gridType', {
options: { '网格': 'lines', '点阵': 'dots' }
});
this.pane.addBinding(this.paneOptions, 'gridColor', { label: '颜色' });
this.pane.addBinding(this.paneOptions, 'gridSize', {
min: 50, max: 500, step: 1, label: '网格大小'
});
this.pane.addBinding(this.paneOptions, 'max_size', {
min: 1, max: 10, step: 0.1, label: '点阵最大值'
});
this.pane.addBinding(this.paneOptions, 'zoomLevel', {
min: this.min_zoom, max: this.max_zoom, step: 1, label: '缩放'
}).on('input', ev => this.onZoomSliderPane(ev.value));
this.pane.addBinding(this.paneOptions, 'viewX', {
min: -1000, max: 1000, step: 1, label: 'X'
});
this.pane.addBinding(this.paneOptions, 'viewY', {
min: -1000, max: 1000, step: 1, label: 'Y'
});
},
methods: {
onWheel(e) {
e.preventDefault();
const rect = e.currentTarget.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
const worldX = mouseX - this.paneOptions.viewX;
const worldY = mouseY - this.paneOptions.viewY;
const speed = Math.min(0.12, 0.06 + Math.log(this.paneOptions.zoomLevel + 1) * 0.015);
const zoomFactor = e.deltaY > 0 ? 1 - speed : 1 + speed;
const oldZoom = this.paneOptions.zoomLevel;
this.paneOptions.zoomLevel = Math.min(this.max_zoom, Math.max(this.min_zoom, oldZoom * zoomFactor));
const actualZoom = this.paneOptions.zoomLevel / oldZoom;
this.paneOptions.viewX = mouseX - worldX * actualZoom;
this.paneOptions.viewY = mouseY - worldY * actualZoom;
this.pane.refresh();
},
onMouseDown(e) {
this.isDragging = true;
this.lastX = e.clientX;
this.lastY = e.clientY;
},
onMouseMove(e) {
if (!this.isDragging) return;
this.paneOptions.viewX += e.clientX - this.lastX;
this.paneOptions.viewY += e.clientY - this.lastY;
this.lastX = e.clientX;
this.lastY = e.clientY;
this.pane.refresh();
},
onMouseUp() { this.isDragging = false; },
onMouseLeave() { this.isDragging = false; },
onZoomSliderPane(newZoom) {
this.paneOptions.zoomLevel = Math.min(this.max_zoom, Math.max(this.min_zoom, newZoom));
}
}
}
</script>
<style scoped>
.grid-root {
width: 100vw;
height: 100vh;
position: relative;
overflow: hidden;
background-color: rgb(225, 224, 224);
user-select: none;
touch-action: none;
font-family: cursive;
}
.pane-container {
position: absolute;
top: 10px;
right: 10px;
z-index: 100;
}
</style>属性说明
- gridType: 网格类型(lines/dots)
- gridColor: 网格颜色
- gridSize: 网格基础大小
- max_size: 线宽/点大小最大值
- zoomLevel: 缩放等级
- viewX/viewY: 视图偏移
