我想我找到了一个与创建一个独立类相关的解决方案,该类首先添加到画布上,以便所有内容都绘制在网格的顶部。
以下是代码:
const GRID_COLOR = "#000000";
const GRID_OPACITY = 0.3;
const CELL_SIZE = 20;
const MAX_SCALE = 2;
const MIN_SCALE = 0.5;
var canvas = new fabric.Canvas('myCanvas', {
backgroundColor: 'rgb(255,165,0)',
selectionColor: 'rgba(100,100,100,0.3)',
selectionLineWidth: 2,
width: window.outerWidth,
height: window.outerHeight,
viewportTransform: [1, 0, 0, 1, -window.outerWidth/2, -window.outerHeight/2],
fireRightClick: true,
fireMiddleClick: true,
});
window.onresize = function() {
canvas.setWidth(window.outerWidth);
canvas.setHeight(window.outerHeight);
};
canvas.on("mouse:wheel", function(opt) {
let zoom = canvas.getZoom();
zoom *= 0.999 ** opt.e.deltaY;
if (zoom > MAX_SCALE) zoom = MAX_SCALE;
if (zoom < MIN_SCALE) zoom = MIN_SCALE;
canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
});
canvas.on("mouse:up", function(opt) {
this.setViewportTransform(this.viewportTransform);
this.isDragging = false;
this.selection = true;
});
canvas.on("mouse:down", function(opt) {
if (opt.e.button === 1) {
this.isDragging = true;
this.selection = false;
this.lastPosX = opt.e.clientX;
this.lastPosY = opt.e.clientY;
}
});
canvas.on("mouse:move", function(opt) {
if (this.isDragging) {
this.viewportTransform[4] += opt.e.clientX - this.lastPosX;
this.viewportTransform[5] += opt.e.clientY - this.lastPosY;
this.lastPosX = opt.e.clientX;
this.lastPosY = opt.e.clientY;
this.requestRenderAll();
}
});
var infBGrid = fabric.util.createClass(fabric.Object, {
type: 'infBGrid',
initialize: function () {
},
render: function (ctx) {
let zoom = canvas.getZoom();
let offX = canvas.viewportTransform[4];
let offY = canvas.viewportTransform[5];
ctx.save();
ctx.strokeStyle = "#cecece";
ctx.lineWidth = 1;
let gridSize = CELL_SIZE * zoom;
const numCellsX = Math.ceil(canvas.width / gridSize);
const numCellsY = Math.ceil(canvas.height / gridSize);
let gridOffsetX = offX % gridSize;
let gridOffsetY = offY % gridSize;
ctx.beginPath();
for (let i = 0; i <= numCellsX; i++) {
let x = gridOffsetX + i * gridSize;
ctx.moveTo((x - offX) / zoom, (0 - offY) / zoom);
ctx.lineTo((x - offX) / zoom, (canvas.height - offY) / zoom);
}
for (let i = 0; i <= numCellsY; i++) {
let y = gridOffsetY + i * gridSize;
ctx.moveTo((0 - offX) / zoom, (y - offY) / zoom);
ctx.lineTo((canvas.width - offX) / zoom, (y - offY) / zoom);
}
ctx.stroke();
ctx.closePath();
ctx.restore();
}
});
var bg = new infBGrid();
canvas.add(bg);
canvas.renderAll();
我猜在渲染方法中创建一个类并编写代码比创建一个在移动视口时必须移动的组更好。
一个不好的解决方案,但也是可行的一个:
const GRID_COLOR = "#000000";
const GRID_OPACITY = 0.3;
const CELL_SIZE = 20;
const MAX_SCALE = 2;
const MIN_SCALE = 0.5;
let grid_size = CELL_SIZE / MIN_SCALE;
var canvas = new fabric.Canvas('myCanvas', {
backgroundColor: 'rgb(255,165,0)',
selectionColor: 'rgba(100,100,100,0.3)',
selectionLineWidth: 2,
width: window.outerWidth,
height: window.outerHeight,
viewportTransform: [1, 0, 0, 1, -window.outerWidth/2, -window.outerHeight/2],
fireRightClick: true,
fireMiddleClick: true,
});
window.onresize = function() {
canvas.setWidth(window.outerWidth);
canvas.setHeight(window.outerHeight);
};
function snapToGrid(point) {
return Math.round(point / grid_size) * grid_size;
}
let backgroundGrid = new fabric.Group([]);
backgroundGrid.excludeFromExport = true;
backgroundGrid.hasControls = false;
backgroundGrid.hasBorders = false;
backgroundGrid.evented = false;
backgroundGrid.selectable = false;
canvas.on("mouse:wheel", function(opt) {
let zoom = canvas.getZoom();
zoom *= 0.999 ** opt.e.deltaY;
if (zoom > MAX_SCALE) zoom = MAX_SCALE;
if (zoom < MIN_SCALE) zoom = MIN_SCALE;
canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
backgroundGrid.setPositionByOrigin(
new fabric.Point (
snapToGrid(-canvas.viewportTransform[4] / canvas.getZoom()),
snapToGrid(-canvas.viewportTransform[5] / canvas.getZoom())
),
"left", "top"
);
});
canvas.on("mouse:down", function(opt) {
if (opt.e.button === 1) {
this.isDragging = true;
this.selection = false;
this.lastPosX = opt.e.clientX;
this.lastPosY = opt.e.clientY;
}
});
canvas.on("mouse:up", function(opt) {
this.setViewportTransform(this.viewportTransform);
this.isDragging = false;
this.selection = true;
});
canvas.on("mouse:move", function(opt) {
if (this.isDragging) {
this.viewportTransform[4] += opt.e.clientX - this.lastPosX;
this.viewportTransform[5] += opt.e.clientY - this.lastPosY;
backgroundGrid.setPositionByOrigin(
new fabric.Point (
snapToGrid(-canvas.viewportTransform[4] / canvas.getZoom()),
snapToGrid(-canvas.viewportTransform[5] / canvas.getZoom())
),
"left", "top"
);
this.requestRenderAll();
this.lastPosX = opt.e.clientX;
this.lastPosY = opt.e.clientY;
}
});
function bgGrid() {
const numCellsX = Math.ceil(canvas.width / MIN_SCALE / grid_size);
const numCellsY = Math.ceil(canvas.height / MIN_SCALE / grid_size);
for (let i = 0; i <= numCellsX; i++) {
const x = i * grid_size;
backgroundGrid.addWithUpdate(new fabric.Line([x,-grid_size,x,canvas.height / MIN_SCALE], {
fill: 'black',
stroke: 'black',
strokeWidth: 1,
opacity: GRID_OPACITY
}));
}
for (let i = 0; i <= numCellsY; i++) {
const y = i * grid_size;
backgroundGrid.addWithUpdate(new fabric.Line([-grid_size,y,canvas.width / MIN_SCALE,y], {
fill: 'black',
stroke: 'black',
strokeWidth: 1,
opacity: GRID_OPACITY
}));
}
}
bgGrid();
canvas.add(backgroundGrid);
我还找到了另一个解决方案,就是重写_renderBackground私有方法。
const GRID_COLOR = "#000000";
const GRID_OPACITY = 0.3;
const CELL_SIZE = 20;
const MAX_SCALE = 2;
const MIN_SCALE = 0.5;
function snapToGrid(point) {
return Math.round(point / CELL_SIZE) * CELL_SIZE;
}
var canvas = new fabric.Canvas('myCanvas', {
backgroundColor: 'rgb(0,0,0)',
selectionColor: 'rgba(100,100,100,0.3)',
selectionLineWidth: 2,
width: window.outerWidth,
height: window.outerHeight,
viewportTransform: [1, 0, 0, 1, -window.outerWidth/2, -window.outerHeight/2],
fireRightClick: true,
fireMiddleClick: true,
});
window.onresize = function() {
canvas.setWidth(window.outerWidth);
canvas.setHeight(window.outerHeight);
};
canvas.on("mouse:wheel", function(opt) {
let zoom = canvas.getZoom();
zoom *= 0.999 ** opt.e.deltaY;
if (zoom > MAX_SCALE) zoom = MAX_SCALE;
if (zoom < MIN_SCALE) zoom = MIN_SCALE;
canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
});
canvas.on("mouse:up", function(opt) {
this.setViewportTransform(this.viewportTransform);
this.isDragging = false;
this.selection = true;
});
canvas.on("mouse:down", function(opt) {
if (opt.e.button === 1) {
this.isDragging = true;
this.selection = false;
this.lastPosX = opt.e.clientX;
this.lastPosY = opt.e.clientY;
}
});
canvas.on("mouse:move", function(opt) {
if (this.isDragging) {
this.viewportTransform[4] += opt.e.clientX - this.lastPosX;
this.viewportTransform[5] += opt.e.clientY - this.lastPosY;
this.lastPosX = opt.e.clientX;
this.lastPosY = opt.e.clientY;
this.requestRenderAll();
}
});
var rec = new fabric.Rect({
left: snapToGrid(canvas.width / 2),
top: snapToGrid(canvas.height / 2),
width: 100,
height: 100,
fill: 'red',
stroke: 'red',
strokeWidth: 2,
selectable: true,
evented: true,
draggable: true,
angle: 0
});
rec.on('mousedown', function(options) {
console.log('Drag start');
});
rec.on('mouseup', function(options) {
console.log('Drag end');
});
rec.on('moving', function(options) {
this.left = snapToGrid(this.left);
this.top = snapToGrid(this.top);
});
canvas._renderBackground = function(ctx) {
if (!this.backgroundColor) {
return;
}
ctx.fillStyle = this.backgroundColor;
ctx.fillRect(
0,
0,
canvas.width,
canvas.height
);
let zoom = this.getZoom();
let offsetX = this.viewportTransform[4];
let offsetY = this.viewportTransform[5];
ctx.strokeStyle = "#cecece";
ctx.lineWidth = 1;
gridSize = CELL_SIZE * zoom;
const numCellsX = Math.ceil(canvas.width / gridSize);
const numCellsY = Math.ceil(canvas.height / gridSize);
gridOffsetX = offsetX % gridSize;
gridOffsetY = offsetY % gridSize;
ctx.save();
ctx.beginPath();
for (let i = 0; i <= numCellsX; i++) {
let x = gridOffsetX + i * gridSize;
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
}
for (let i = 0; i <= numCellsY; i++) {
let y = gridOffsetY + i * gridSize;
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
}
ctx.stroke();
ctx.closePath();
ctx.restore();
}
canvas.add(rec);
canvas.renderAll();