-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(): Convert align guidelines to TS. Add build system for extensions #10043
Merged
Merged
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
eb34b51
feat: add v6 aligning guidelines
zhe-he c8f9aa2
Merge branch 'master' into v6-aligning-guidelines
asturur ade2d84
moved to extensions
asturur 3c3c214
Merge branch 'master' into v6-aligning-guidelines
asturur 1e3c227
works
asturur a238593
ops
asturur 3b5211d
Added feature readme
asturur 194fd74
build
asturur 3f8f821
try this
asturur 734ee4b
prettier
asturur 001f6ca
Merge branch 'master' into v6-aligning-guidelines
asturur 7033532
extend lint to work on extensions folder
asturur 1e22e21
removed barrel import
asturur cf6fcd4
added more readme
asturur 3949818
more build
asturur a86afdc
ad basic poc test
asturur 4dac5f3
Merge branch 'master' into v6-aligning-guidelines
asturur 2fb78f0
e2e running with extra import
asturur a27afe0
actions
asturur e1b0f48
better
asturur d4f0f14
removed
asturur 6d73114
changes
asturur 92e2d8c
a test running
asturur 7b8c044
a test running
asturur 79045c9
working test
asturur 0666948
working test
asturur File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
export type VerticalLine = { | ||
x: number; | ||
y1: number; | ||
y2: number; | ||
}; | ||
|
||
export type HorizontalLine = { | ||
y: number; | ||
x1: number; | ||
x2: number; | ||
}; | ||
|
||
export type VerticalLineProps = { | ||
x: number; | ||
objectY: number; | ||
objectHeight: number; | ||
activeObjectY: number; | ||
activeObjectHeight: number; | ||
}; | ||
|
||
export type HorizontalLineProps = { | ||
y: number; | ||
objectX: number; | ||
objectWidth: number; | ||
activeObjectX: number; | ||
activeObjectWidth: number; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export const AligningLineConfig = { | ||
offset: 0, | ||
/** At what distance from the shape does alignment begin? */ | ||
margin: 4, | ||
/** Aligning line dimensions */ | ||
width: 1, | ||
/** Aligning line color */ | ||
color: "rgb(255,0,0,0.9)" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import type { BasicTransformEvent, Canvas, FabricObject, TBBox, TPointerEvent } from "../../fabric"; | ||
import { Point } from "../../src/Point"; | ||
import { | ||
collectHorizontalPoint, | ||
collectLine, | ||
collectVerticalPoint, | ||
drawHorizontalLine, | ||
drawPointList, | ||
drawVerticalLine, | ||
getObjectsByTarget, | ||
} from "./util"; | ||
import { makeBoundingBoxFromPoints } from "../../src/util/misc/boundingBoxFromPoints"; | ||
import { HorizontalLine, VerticalLine } from "./@types"; | ||
import { AligningLineConfig } from "./constant"; | ||
|
||
type TransformEvent = BasicTransformEvent<TPointerEvent> & { target: FabricObject }; | ||
type AlignOptions = Partial<typeof AligningLineConfig>; | ||
export function initAligningGuidelines(canvas: Canvas, options: AlignOptions = {}) { | ||
Object.assign(AligningLineConfig, options); | ||
|
||
const horizontalLines = new Set<string>(); | ||
const verticalLines = new Set<string>(); | ||
let onlyDrawPoint = false; | ||
const cacheMap = new Map<string, [TBBox, Point[]]>(); | ||
|
||
const getCaCheMapValue = (object: FabricObject) => { | ||
const cacheKey = [object.calcTransformMatrix().toString(), object.width, object.height].join(); | ||
const cacheValue = cacheMap.get(cacheKey); | ||
if (cacheValue) return cacheValue; | ||
const coords = object.getCoords(); | ||
const rect = makeBoundingBoxFromPoints(coords); | ||
const value: [TBBox, Point[]] = [rect, coords]; | ||
cacheMap.set(cacheKey, value); | ||
return value; | ||
}; | ||
|
||
function moving(e: TransformEvent) { | ||
const activeObject = e.target; | ||
activeObject.setCoords(); | ||
onlyDrawPoint = false; | ||
verticalLines.clear(); | ||
horizontalLines.clear(); | ||
|
||
const objects = getObjectsByTarget(activeObject); | ||
const activeObjectRect = activeObject.getBoundingRect(); | ||
|
||
for (const object of objects) { | ||
const objectRect = getCaCheMapValue(object)[0]; | ||
const { vLines, hLines } = collectLine({ activeObject, activeObjectRect, objectRect }); | ||
vLines.forEach((o) => { | ||
verticalLines.add(JSON.stringify(o)); | ||
}); | ||
hLines.forEach((o) => { | ||
horizontalLines.add(JSON.stringify(o)); | ||
}); | ||
} | ||
} | ||
|
||
function scalingOrResizing(e: TransformEvent) { | ||
// br bl tr tl mb ml mt mr | ||
const activeObject = e.target; | ||
activeObject.setCoords(); | ||
const isScale = String(e.transform.action).startsWith("scale"); | ||
verticalLines.clear(); | ||
horizontalLines.clear(); | ||
|
||
const objects = getObjectsByTarget(activeObject); | ||
let corner = e.transform.corner; | ||
if (activeObject.flipX) corner = corner.replace("l", "r").replace("r", "l"); | ||
if (activeObject.flipY) corner = corner.replace("t", "b").replace("b", "t"); | ||
let index = ["tl", "tr", "br", "bl", "mt", "mr", "mb", "ml"].indexOf(corner); | ||
if (index == -1) return; | ||
onlyDrawPoint = index > 3; | ||
if (onlyDrawPoint) { | ||
const angle = activeObject.getTotalAngle(); | ||
if (angle % 90 != 0) return; | ||
index -= 4; | ||
} | ||
let point = activeObject.getCoords()[index]; | ||
for (const object of objects) { | ||
const [rect, coords] = getCaCheMapValue(object); | ||
const center = new Point(rect.left + rect.width / 2, rect.top + rect.height / 2); | ||
const list = [...coords, center]; | ||
const props = { activeObject, point, list, isScale, index }; | ||
const vLines = collectVerticalPoint(props); | ||
const hLines = collectHorizontalPoint(props); | ||
vLines.forEach((o) => { | ||
verticalLines.add(JSON.stringify(o)); | ||
}); | ||
hLines.forEach((o) => { | ||
horizontalLines.add(JSON.stringify(o)); | ||
}); | ||
if (vLines.length || hLines.length) point = activeObject.getCoords()[index]; | ||
} | ||
} | ||
|
||
function beforeRender() { | ||
canvas.clearContext(canvas.contextTop); | ||
} | ||
function afterRender() { | ||
if (onlyDrawPoint) { | ||
const list: Array<VerticalLine | HorizontalLine> = []; | ||
for (const v of verticalLines) list.push(JSON.parse(v)); | ||
for (const h of horizontalLines) list.push(JSON.parse(h)); | ||
drawPointList(canvas, list); | ||
} else { | ||
for (const v of verticalLines) drawVerticalLine(canvas, JSON.parse(v)); | ||
for (const h of horizontalLines) drawHorizontalLine(canvas, JSON.parse(h)); | ||
} | ||
} | ||
function mouseUp() { | ||
verticalLines.clear(); | ||
horizontalLines.clear(); | ||
cacheMap.clear(); | ||
canvas.requestRenderAll(); | ||
} | ||
|
||
canvas.on("object:resizing", scalingOrResizing); | ||
canvas.on("object:scaling", scalingOrResizing); | ||
canvas.on("object:moving", moving); | ||
canvas.on("before:render", beforeRender); | ||
canvas.on("after:render", afterRender); | ||
canvas.on("mouse:up", mouseUp); | ||
|
||
return () => { | ||
canvas.off("object:resizing", scalingOrResizing); | ||
canvas.off("object:scaling", scalingOrResizing); | ||
canvas.off("object:moving", moving); | ||
canvas.off("before:render", beforeRender); | ||
canvas.off("after:render", afterRender); | ||
canvas.off("mouse:up", mouseUp); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import type { FabricObject, Point } from "../../../fabric"; | ||
|
||
export function getDistance(a: number, b: number) { | ||
return Math.abs(a - b); | ||
} | ||
|
||
export function setPositionDir(target: FabricObject, pos: Point, dir: "x" | "y") { | ||
const center = target.translateToCenterPoint(pos, "center", "center"); | ||
const position = target.translateToOriginPoint(center, target.originX, target.originY); | ||
if (dir == "x") target.setX(position.x); | ||
else target.setY(position.y); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
import type { FabricObject, TBBox } from "../../../fabric"; | ||
import { Point } from "../../../src/Point"; | ||
import { HorizontalLine, VerticalLine } from "../@types"; | ||
import { AligningLineConfig } from "../constant"; | ||
import { getDistance, setPositionDir } from "./basic"; | ||
|
||
type CollectLineProps = { | ||
activeObject: FabricObject; | ||
activeObjectRect: TBBox; | ||
objectRect: TBBox; | ||
}; | ||
|
||
export function collectLine(props: CollectLineProps) { | ||
const aligningLineMargin = AligningLineConfig.margin; | ||
const { activeObject, activeObjectRect, objectRect } = props; | ||
const list = makeLineByRect(objectRect); | ||
const aList = makeLineByRect(activeObjectRect); | ||
const margin = aligningLineMargin / (activeObject.canvas?.getZoom() ?? 1); | ||
const opts = { target: activeObject, list, aList, margin }; | ||
const vLines = collectVerticalLine(opts); | ||
const hLines = collectHorizontalLine(opts); | ||
|
||
return { vLines, hLines }; | ||
} | ||
|
||
type CollectItemLineProps = { | ||
target: FabricObject; | ||
list: LineProps[]; | ||
aList: LineProps[]; | ||
margin: number; | ||
}; | ||
function collectVerticalLine(props: CollectItemLineProps) { | ||
const { target, list, aList, margin } = props; | ||
|
||
const arr = aList.map((x) => getDistanceLine(x, list, "x")); | ||
const min = Math.min(...arr.map((x) => x.dis)); | ||
if (min > margin) return []; | ||
const lines: VerticalLine[] = []; | ||
const width = aList[0].x2 - aList[0].x; | ||
const height = aList[0].y2 - aList[0].y; | ||
let b = false; | ||
for (let i = 0; i < arr.length; i++) { | ||
const item = arr[i]; | ||
if (min == item.dis) { | ||
const line = list[item.index]; | ||
const aLine = aList[item.index]; | ||
let x = line.x; | ||
const y = aLine.y; | ||
|
||
const y1 = Math.min(line.y, line.y2, y, aLine.y2); | ||
const y2 = Math.max(line.y, line.y2, y, aLine.y2); | ||
// 参考线可画多条 | ||
lines.push({ x, y1, y2 }); | ||
if (b) continue; | ||
b = true; | ||
// 对齐只进行一次 | ||
setPos({ target, x, y, centerX: i - 1, centerY: item.index - 1, width, height, dir: "x" }); | ||
const dis = min * item.dir; | ||
aList.forEach((x) => (x.x -= dis)); | ||
} | ||
} | ||
return lines; | ||
} | ||
|
||
function collectHorizontalLine(props: CollectItemLineProps) { | ||
const { target, list, aList, margin } = props; | ||
|
||
const arr = aList.map((x) => getDistanceLine(x, list, "y")); | ||
const min = Math.min(...arr.map((x) => x.dis)); | ||
if (min > margin) return []; | ||
const lines: HorizontalLine[] = []; | ||
const width = aList[0].x2 - aList[0].x; | ||
const height = aList[0].y2 - aList[0].y; | ||
let b = false; | ||
for (let i = 0; i < arr.length; i++) { | ||
const item = arr[i]; | ||
if (min == item.dis) { | ||
const line = list[item.index]; | ||
const aLine = aList[item.index]; | ||
let y = line.y; | ||
const x = aLine.x; | ||
|
||
const x1 = Math.min(line.x, line.x2, x, aLine.x2); | ||
const x2 = Math.max(line.x, line.x2, x, aLine.x2); | ||
// 参考线可画多条 | ||
lines.push({ y, x1, x2 }); | ||
if (b) continue; | ||
b = true; | ||
// 对齐只进行一次 | ||
setPos({ target, x, y, centerX: item.index - 1, centerY: i - 1, width, height, dir: "y" }); | ||
const dis = min * item.dir; | ||
aList.forEach((x) => (x.y -= dis)); | ||
} | ||
} | ||
return lines; | ||
} | ||
|
||
type LineProps = { | ||
x: number; | ||
y: number; | ||
x2: number; | ||
y2: number; | ||
}; | ||
function getDistanceLine(target: LineProps, list: LineProps[], type: "x" | "y") { | ||
let dis = Infinity; | ||
let index = -1; | ||
/** 1正值 -1负值 */ | ||
let dir = 1; | ||
for (let i = 0; i < list.length; i++) { | ||
const v = getDistance(target[type], list[i][type]); | ||
if (dis > v) { | ||
index = i; | ||
dis = v; | ||
dir = target[type] > list[i][type] ? 1 : -1; | ||
} | ||
} | ||
return { dis, index, dir }; | ||
} | ||
|
||
function makeLineByRect(rect: TBBox) { | ||
const { left, top, width, height } = rect; | ||
const a = { x: left, y: top, x2: left + width, y2: top + height }; | ||
const x = left + width / 2; | ||
const y = top + height / 2; | ||
const b = { x, y, x2: x, y2: y }; | ||
const c = { x: left + width, x2: left, y: top + height, y2: top }; | ||
|
||
return [a, b, c]; | ||
} | ||
|
||
type SnapToPixelProps = { | ||
target: FabricObject; | ||
x: number; | ||
y: number; | ||
/** -1 0 1 */ | ||
centerX: number; | ||
/** -1 0 1 */ | ||
centerY: number; | ||
width: number; | ||
height: number; | ||
dir: "x" | "y"; | ||
}; | ||
function setPos(props: SnapToPixelProps) { | ||
const { target, centerX, centerY, width, height, dir } = props; | ||
let { x, y } = props; | ||
x -= (centerX * width) / 2; | ||
y -= (centerY * height) / 2; | ||
setPositionDir(target, new Point(x, y), dir); | ||
target.setCoords(); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the issue i want to solve. I don't want to package those in this way.
I ll need to work out a way for rollup to produce a different bundle that has fabric has an external dep and that is reacheable from fabric/plugin or something like that