对象捕捉到 fabric.js 上的自定义网格
Objects snap to custum grid on fabric.js
我正在构建一个应用程序,我需要将对象捕捉到 mouseMove 上的网格,但我不需要常规网格,而是自定义网格。我找到了这个 Fiddle 并尝试放置我自己的网格,但对象没有捕捉到它。我需要的是对象的中心捕捉到由两条网格线形成的十字。
这是我的代码:
var canvas = new fabric.Canvas('c', { selection: false });
var grid = 20;
// create grid
for (var i = 0; i < (canvas.height / grid); i++) {
canvas.add(new fabric.Path('m 260.75,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 309.75,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 353.75,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 391.25,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 419.25,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 435.25,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 216.5,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 179,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 151,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 135,122.82188 0,349.31925 ', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,147.25 348.63852,0', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,161.25 348.63852,0', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,188 348.63852,0', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,224.75 348.63852,0', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,269.75 348.63852,0', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,326.05147 348.63852,0', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,370.83102 348.63852,0', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,407.75 348.63852,0', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,434.25 348.63852,0', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,448.5 348.63852,0', { stroke: '#ccc', selectable: false }));
}
// add objects
canvas.add(new fabric.Rect({
left: 100,
top: 100,
width: 20,
height: 20,
fill: '#faa',
originX: 'center', originY: 'center',
centeredRotation: true
}));
// snap to grid
canvas.on('object:moving', function(options) {
options.target.set({
left: Math.round(options.target.left / grid) * grid,
top: Math.round(options.target.top / grid) * grid
});
});
<html>
<head>
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
</head>
<body>
<canvas id="c" width="600" height="600"></canvas>
如果有人能提供帮助,我们将不胜感激。
再次分析后,我发现 svg 正在吸附到此 var
var grid = 20;
中可能给出的网格,而不是我使用 canvas.add(new fabric.Path('m 110.91682,147.25 348.63852,0', { stroke: '#ccc', selectable: false }));
创建的网格。这可能是罪魁祸首,但我不确定。
确实,var grid = 20
正是导致您出现问题的原因,因为您的网格与 spaced 不同。您已经静态创建了网格,但是使用定义起点和间距的对象会更加动态,并允许您使用同一对象将事件捕捉到动态创建的网格点。
我能够使用 对象数组 作为 网格间距 并实例化两个 start 和 coords 对象,其中包含 x 和 的数组y 将线放入每个坐标键下的坐标对象中。
coords对象其实就是从起始位置开始每行之间的space。 start 对象是在 x[= 中绘制的线条边缘的起始位置81=] 和 y 轴.
我们将使用这些对象来动态创建网格和一个 points 对象以供稍后参考,以便将鼠标移动到网格.
使用 gridSpacing[0]['start']['x']
和 gridSpacing[0]['start']['y']
定义起点。然后是两个数组来保存动态创建的 x 轴线 和 y 轴线 - xArr
和 yArr
的值.
然后我们创建一个嵌套的 for 循环,遍历 xArr 和 yArr 创建一个 points 对象。
另外两个 for 循环使用 xArr* 和 yArr 动态创建网格数组。
最后是 object:moving
事件中的捕捉部分。实例化一个轴对象,它将 the event.target.left
和 event.target.top
属性作为 key/value 对。我们使用它来遍历 points 对象并使用条件检查鼠标移动事件相对于点对象的位置 key/values => keys for the x axis and values 用于 y 轴。
var canvas = new fabric.Canvas('c', {
selection: false
});
// insantiate object for starting and coordinates of spacing
const gridSpacing = [{
start: {
x: 111,
y: 123.25
},
coords: {
x: [24, 16, 28, 37.5, 44.25, 49, 44, 37.5, 28, 16],
y: [24, 16, 28, 37.5, 44.25, 49, 44, 37.5, 28, 16]
}
}]
// create grid
// define the starting coords
let xCoords = gridSpacing[0]['start']['x'];
let yCoords = gridSpacing[0]['start']['y'];
// instantiate x axis and y axis arrays
let xArr = []
let yArr = []
// fill xArr with values using gridSpacing object
for (let x = 0; x < gridSpacing[0].coords['x'].length; x++) {
let spacing = gridSpacing[0].coords['x'][x]
xArr.push(xCoords += spacing)
}
// fill yArr with values using gridSpacing object
for (let y = 0; y < gridSpacing[0].coords['y'].length; y++) {
let spacing = gridSpacing[0].coords['y'][y]
yArr.push(yCoords += spacing)
}
// instantiate points obj using xArr and yArr for in loops
const points = []
for (let i in xArr) {
for (let y in yArr) {
points.push({
[xArr[i]]: yArr[y]
})
}
}
/* EDIT: This is the section that draws the grid lines. No need for it as you are using the points object and xArr and yArr to make your mouse snap to the spacing.
// create and draw x axis grid dynamically using xArr
for (let x = 0; x < xArr.length; x++) {
canvas.add(new fabric.Path(`m ${xArr[x]},122.82188 0,349.31925`, {
stroke: '#ccc',
selectable: false
}));
}
// create and draw y axis grid dynamically using yArr
for (let y = 0; y < gridSpacing[0].coords['y'].length; y++) {
canvas.add(new fabric.Path(`m 110.91682,${yArr[y]} 348.63852,0`, {
stroke: '#ccc',
selectable: false
}));
}
*/
// add objects
canvas.add(new fabric.Rect({
left: 100,
top: 100,
width: 20,
height: 20,
fill: '#faa',
originX: 'center',
originY: 'center',
centeredRotation: true
}));
canvas.add(new fabric.Circle({
left: xArr[5],
top: yArr[5],
radius: 60,
fill: '#9f9',
originX: 'center',
originY: 'center',
centeredRotation: true
}));
// snap to grid
canvas.on('object:moving', function(event) {
// create objbect that will hold current mouse move events x and y
let axis = {
[event.target.left]: event.target.top
}
let x, y = '';
// iterate over our points and check event.target values
points.forEach((point, i) => {
// conditional to check x axis boundaries and point variations
// left
if (parseInt(Object.keys(axis)) < xArr[1] - xArr[1] * .1 || parseInt(Object.keys(axis)) < xArr[0]) {
x = xArr[0]
// right
}else if (parseInt(Object.keys(axis)) > xArr[xArr.length - 1]) {
x = xArr[xArr.length - 1]
// boundaries
}else if (Object.keys(axis) < Object.keys(points[i]) && Object.keys(axis) > Object.keys(points[i - 1]) && Object.keys(axis) > xArr[0]) {
x = Object.keys(points[i])
}
// conditional to check y axis boundaries and point variations
// up
if (parseInt(Object.values(axis)) < yArr[1] - yArr[1] * .1 || parseInt(Object.values(axis)) < yArr[0]) {
y = yArr[0]
// down
}else if (parseInt(Object.values(axis)) > yArr[yArr.length - 1]) {
y = yArr[yArr.length - 1]
// boundaries
}else if (Object.values(axis) < Object.values(points[i]) && Object.values(axis) > Object.values(points[i - 1]) && Object.values(axis) > xArr[0]) {
y = Object.values(points[i])
}
})
// set event.target left and top properties
event.target.set({
left: parseInt(x),
top: parseInt(y)
})
})
<html>
<head>
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
</head>
<body>
<canvas id="c" width="600" height="600"></canvas>
</body>
首先
希望您的 html
结构在您的本地代码中没有问题。
其次
您正在 运行ning 一个 for 循环,该循环实际绘制同一条线 30 次。您可以在一个函数中执行此操作,并且 运行 只需执行一次:
function createGrid() {
canvas.add(new fabric.Path('m 260.75,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 309.75,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
...
}
//then run it once:
createGrid();
由于您的网格没有以不规则的方式间隔,因此您不需要:
var grid = 20;
我的方法是检索所有水平和垂直捕捉点并找到最近的 x 和 y 轴来进行捕捉。
像这样:
const horizontalSnappingPoints = options.target.canvas._objects
.filter((el, index) => options.target.canvas._objects
.some((sameTopEl, sameTopElIndex) => sameTopEl.top === el.top && sameTopElIndex !== index)).map(item => item.left);
垂直的也一样,然后在设置left/top属性的同时根据每个方向找到最近的点。
left: horizontalSnappingPoints.reduce(function (prev, curr) {
return (Math.abs(curr - options.target.left) < Math.abs(prev - options.target.left) ? curr : prev);
片段
这是最后的片段:
var canvas = new fabric.Canvas('c', {
selection: false
});
// create grid
function createGrid() {
canvas.add(new fabric.Path('m 260.75,122.82188 0,349.31925', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 309.75,122.82188 0,349.31925', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 353.75,122.82188 0,349.31925', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 391.25,122.82188 0,349.31925', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 419.25,122.82188 0,349.31925', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 435.25,122.82188 0,349.31925', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 216.5,122.82188 0,349.31925', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 179,122.82188 0,349.31925', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 151,122.82188 0,349.31925', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 135,122.82188 0,349.31925 ', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,147.25 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,161.25 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,188 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,224.75 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,269.75 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,326.05147 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,370.83102 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,407.75 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,434.25 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,448.5 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
}
// call this to actually create grids
createGrid();
// add objects
canvas.add(new fabric.Rect({
left: 100,
top: 100,
width: 20,
height: 20,
fill: '#faa',
originX: 'center',
originY: 'center',
centeredRotation: true
}));
canvas.add(new fabric.Circle({
left: 300,
top: 300,
radius: 60,
fill: '#9f9',
originX: 'center',
originY: 'center',
centeredRotation: true
}));
// snap to grid
canvas.on('object:moving', function(options) {
let horizontalSnappingPoints = options.target.canvas._objects.filter(item => item.path)
horizontalSnappingPoints = horizontalSnappingPoints.filter((el, index) => horizontalSnappingPoints
.some((sameTopEl, sameTopElIndex) => sameTopEl.top === el.top && sameTopElIndex !== index)).map(item => item.left);
let verticalSnappingPoints = options.target.canvas._objects.filter(item => item.path)
verticalSnappingPoints = verticalSnappingPoints.filter((el, index) => verticalSnappingPoints
.some((sameLeftEl, sameLeftElIndex) => sameLeftEl.left === el.left && sameLeftElIndex !== index)).map(item => item.top)
options.target.set({
left: horizontalSnappingPoints.reduce(function(prev, curr) {
return (Math.abs(curr - options.target.left) < Math.abs(prev - options.target.left) ? curr : prev);
}),
top: verticalSnappingPoints.reduce(function(prev, curr) {
return (Math.abs(curr - options.target.top) < Math.abs(prev - options.target.top) ? curr : prev);
}),
});
});
<html>
<head>
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
</head>
<body>
<canvas id="c" width="600" height="600"></canvas>
</body>
</html>
我正在构建一个应用程序,我需要将对象捕捉到 mouseMove 上的网格,但我不需要常规网格,而是自定义网格。我找到了这个 Fiddle 并尝试放置我自己的网格,但对象没有捕捉到它。我需要的是对象的中心捕捉到由两条网格线形成的十字。
这是我的代码:
var canvas = new fabric.Canvas('c', { selection: false });
var grid = 20;
// create grid
for (var i = 0; i < (canvas.height / grid); i++) {
canvas.add(new fabric.Path('m 260.75,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 309.75,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 353.75,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 391.25,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 419.25,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 435.25,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 216.5,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 179,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 151,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 135,122.82188 0,349.31925 ', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,147.25 348.63852,0', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,161.25 348.63852,0', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,188 348.63852,0', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,224.75 348.63852,0', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,269.75 348.63852,0', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,326.05147 348.63852,0', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,370.83102 348.63852,0', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,407.75 348.63852,0', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,434.25 348.63852,0', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 110.91682,448.5 348.63852,0', { stroke: '#ccc', selectable: false }));
}
// add objects
canvas.add(new fabric.Rect({
left: 100,
top: 100,
width: 20,
height: 20,
fill: '#faa',
originX: 'center', originY: 'center',
centeredRotation: true
}));
// snap to grid
canvas.on('object:moving', function(options) {
options.target.set({
left: Math.round(options.target.left / grid) * grid,
top: Math.round(options.target.top / grid) * grid
});
});
<html>
<head>
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
</head>
<body>
<canvas id="c" width="600" height="600"></canvas>
如果有人能提供帮助,我们将不胜感激。
再次分析后,我发现 svg 正在吸附到此 var
var grid = 20;
中可能给出的网格,而不是我使用 canvas.add(new fabric.Path('m 110.91682,147.25 348.63852,0', { stroke: '#ccc', selectable: false }));
创建的网格。这可能是罪魁祸首,但我不确定。
确实,var grid = 20
正是导致您出现问题的原因,因为您的网格与 spaced 不同。您已经静态创建了网格,但是使用定义起点和间距的对象会更加动态,并允许您使用同一对象将事件捕捉到动态创建的网格点。
我能够使用 对象数组 作为 网格间距 并实例化两个 start 和 coords 对象,其中包含 x 和 的数组y 将线放入每个坐标键下的坐标对象中。
coords对象其实就是从起始位置开始每行之间的space。 start 对象是在 x[= 中绘制的线条边缘的起始位置81=] 和 y 轴.
我们将使用这些对象来动态创建网格和一个 points 对象以供稍后参考,以便将鼠标移动到网格.
使用 gridSpacing[0]['start']['x']
和 gridSpacing[0]['start']['y']
定义起点。然后是两个数组来保存动态创建的 x 轴线 和 y 轴线 - xArr
和 yArr
的值.
然后我们创建一个嵌套的 for 循环,遍历 xArr 和 yArr 创建一个 points 对象。
另外两个 for 循环使用 xArr* 和 yArr 动态创建网格数组。
最后是 object:moving
事件中的捕捉部分。实例化一个轴对象,它将 the event.target.left
和 event.target.top
属性作为 key/value 对。我们使用它来遍历 points 对象并使用条件检查鼠标移动事件相对于点对象的位置 key/values => keys for the x axis and values 用于 y 轴。
var canvas = new fabric.Canvas('c', {
selection: false
});
// insantiate object for starting and coordinates of spacing
const gridSpacing = [{
start: {
x: 111,
y: 123.25
},
coords: {
x: [24, 16, 28, 37.5, 44.25, 49, 44, 37.5, 28, 16],
y: [24, 16, 28, 37.5, 44.25, 49, 44, 37.5, 28, 16]
}
}]
// create grid
// define the starting coords
let xCoords = gridSpacing[0]['start']['x'];
let yCoords = gridSpacing[0]['start']['y'];
// instantiate x axis and y axis arrays
let xArr = []
let yArr = []
// fill xArr with values using gridSpacing object
for (let x = 0; x < gridSpacing[0].coords['x'].length; x++) {
let spacing = gridSpacing[0].coords['x'][x]
xArr.push(xCoords += spacing)
}
// fill yArr with values using gridSpacing object
for (let y = 0; y < gridSpacing[0].coords['y'].length; y++) {
let spacing = gridSpacing[0].coords['y'][y]
yArr.push(yCoords += spacing)
}
// instantiate points obj using xArr and yArr for in loops
const points = []
for (let i in xArr) {
for (let y in yArr) {
points.push({
[xArr[i]]: yArr[y]
})
}
}
/* EDIT: This is the section that draws the grid lines. No need for it as you are using the points object and xArr and yArr to make your mouse snap to the spacing.
// create and draw x axis grid dynamically using xArr
for (let x = 0; x < xArr.length; x++) {
canvas.add(new fabric.Path(`m ${xArr[x]},122.82188 0,349.31925`, {
stroke: '#ccc',
selectable: false
}));
}
// create and draw y axis grid dynamically using yArr
for (let y = 0; y < gridSpacing[0].coords['y'].length; y++) {
canvas.add(new fabric.Path(`m 110.91682,${yArr[y]} 348.63852,0`, {
stroke: '#ccc',
selectable: false
}));
}
*/
// add objects
canvas.add(new fabric.Rect({
left: 100,
top: 100,
width: 20,
height: 20,
fill: '#faa',
originX: 'center',
originY: 'center',
centeredRotation: true
}));
canvas.add(new fabric.Circle({
left: xArr[5],
top: yArr[5],
radius: 60,
fill: '#9f9',
originX: 'center',
originY: 'center',
centeredRotation: true
}));
// snap to grid
canvas.on('object:moving', function(event) {
// create objbect that will hold current mouse move events x and y
let axis = {
[event.target.left]: event.target.top
}
let x, y = '';
// iterate over our points and check event.target values
points.forEach((point, i) => {
// conditional to check x axis boundaries and point variations
// left
if (parseInt(Object.keys(axis)) < xArr[1] - xArr[1] * .1 || parseInt(Object.keys(axis)) < xArr[0]) {
x = xArr[0]
// right
}else if (parseInt(Object.keys(axis)) > xArr[xArr.length - 1]) {
x = xArr[xArr.length - 1]
// boundaries
}else if (Object.keys(axis) < Object.keys(points[i]) && Object.keys(axis) > Object.keys(points[i - 1]) && Object.keys(axis) > xArr[0]) {
x = Object.keys(points[i])
}
// conditional to check y axis boundaries and point variations
// up
if (parseInt(Object.values(axis)) < yArr[1] - yArr[1] * .1 || parseInt(Object.values(axis)) < yArr[0]) {
y = yArr[0]
// down
}else if (parseInt(Object.values(axis)) > yArr[yArr.length - 1]) {
y = yArr[yArr.length - 1]
// boundaries
}else if (Object.values(axis) < Object.values(points[i]) && Object.values(axis) > Object.values(points[i - 1]) && Object.values(axis) > xArr[0]) {
y = Object.values(points[i])
}
})
// set event.target left and top properties
event.target.set({
left: parseInt(x),
top: parseInt(y)
})
})
<html>
<head>
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
</head>
<body>
<canvas id="c" width="600" height="600"></canvas>
</body>
首先
希望您的 html
结构在您的本地代码中没有问题。
其次
您正在 运行ning 一个 for 循环,该循环实际绘制同一条线 30 次。您可以在一个函数中执行此操作,并且 运行 只需执行一次:
function createGrid() {
canvas.add(new fabric.Path('m 260.75,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
canvas.add(new fabric.Path('m 309.75,122.82188 0,349.31925', { stroke: '#ccc', selectable: false }));
...
}
//then run it once:
createGrid();
由于您的网格没有以不规则的方式间隔,因此您不需要:
var grid = 20;
我的方法是检索所有水平和垂直捕捉点并找到最近的 x 和 y 轴来进行捕捉。
像这样:
const horizontalSnappingPoints = options.target.canvas._objects
.filter((el, index) => options.target.canvas._objects
.some((sameTopEl, sameTopElIndex) => sameTopEl.top === el.top && sameTopElIndex !== index)).map(item => item.left);
垂直的也一样,然后在设置left/top属性的同时根据每个方向找到最近的点。
left: horizontalSnappingPoints.reduce(function (prev, curr) {
return (Math.abs(curr - options.target.left) < Math.abs(prev - options.target.left) ? curr : prev);
片段
这是最后的片段:
var canvas = new fabric.Canvas('c', {
selection: false
});
// create grid
function createGrid() {
canvas.add(new fabric.Path('m 260.75,122.82188 0,349.31925', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 309.75,122.82188 0,349.31925', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 353.75,122.82188 0,349.31925', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 391.25,122.82188 0,349.31925', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 419.25,122.82188 0,349.31925', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 435.25,122.82188 0,349.31925', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 216.5,122.82188 0,349.31925', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 179,122.82188 0,349.31925', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 151,122.82188 0,349.31925', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 135,122.82188 0,349.31925 ', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,147.25 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,161.25 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,188 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,224.75 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,269.75 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,326.05147 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,370.83102 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,407.75 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,434.25 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Path('m 110.91682,448.5 348.63852,0', {
stroke: '#ccc',
selectable: false
}));
}
// call this to actually create grids
createGrid();
// add objects
canvas.add(new fabric.Rect({
left: 100,
top: 100,
width: 20,
height: 20,
fill: '#faa',
originX: 'center',
originY: 'center',
centeredRotation: true
}));
canvas.add(new fabric.Circle({
left: 300,
top: 300,
radius: 60,
fill: '#9f9',
originX: 'center',
originY: 'center',
centeredRotation: true
}));
// snap to grid
canvas.on('object:moving', function(options) {
let horizontalSnappingPoints = options.target.canvas._objects.filter(item => item.path)
horizontalSnappingPoints = horizontalSnappingPoints.filter((el, index) => horizontalSnappingPoints
.some((sameTopEl, sameTopElIndex) => sameTopEl.top === el.top && sameTopElIndex !== index)).map(item => item.left);
let verticalSnappingPoints = options.target.canvas._objects.filter(item => item.path)
verticalSnappingPoints = verticalSnappingPoints.filter((el, index) => verticalSnappingPoints
.some((sameLeftEl, sameLeftElIndex) => sameLeftEl.left === el.left && sameLeftElIndex !== index)).map(item => item.top)
options.target.set({
left: horizontalSnappingPoints.reduce(function(prev, curr) {
return (Math.abs(curr - options.target.left) < Math.abs(prev - options.target.left) ? curr : prev);
}),
top: verticalSnappingPoints.reduce(function(prev, curr) {
return (Math.abs(curr - options.target.top) < Math.abs(prev - options.target.top) ? curr : prev);
}),
});
});
<html>
<head>
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
</head>
<body>
<canvas id="c" width="600" height="600"></canvas>
</body>
</html>