From 10e917070aac070c1e77b4a8c9a55c1afccee5be Mon Sep 17 00:00:00 2001 From: "yuqi.pyq" Date: Mon, 15 Jan 2024 21:26:14 +0800 Subject: [PATCH 01/23] fix: remove defXY and cx/cy should not affect transform #1624 --- __tests__/demos/2d/circle.ts | 25 +- __tests__/demos/2d/clippath.ts | 82 + __tests__/demos/2d/ellipse.ts | 24 +- __tests__/demos/2d/gradient.ts | 125 ++ __tests__/demos/2d/image.ts | 17 +- __tests__/demos/2d/index.ts | 5 +- __tests__/demos/2d/path.ts | 2 +- __tests__/demos/2d/pattern.ts | 199 ++ __tests__/demos/2d/rect.ts | 13 +- __tests__/demos/2d/transform.ts | 117 ++ __tests__/demos/3d/force.ts | 1769 +++++++++++++++++ __tests__/demos/3d/index.ts | 1 + __tests__/demos/animation/index.ts | 4 + __tests__/demos/animation/line-dash.ts | 37 + __tests__/demos/animation/marching-ants.ts | 43 + .../multiple-animations-per-element.ts | 49 + __tests__/demos/animation/offset-path.ts | 118 ++ __tests__/demos/bugfix/1624.ts | 17 + __tests__/demos/bugfix/index.ts | 1 + __tests__/demos/event/circle.ts | 58 + __tests__/demos/event/ellipse.ts | 62 + .../image-non-transparent-pixel.ts | 1 + __tests__/demos/event/index.ts | 3 + __tests__/demos/perf/circles.ts | 35 + __tests__/demos/perf/index.ts | 1 + __tests__/demos/plugin/matterjs.ts | 22 +- __tests__/demos/plugin/rough-barchart.ts | 15 +- __tests__/main.ts | 25 +- package.json | 2 + packages/g-lite/src/css/StyleValueRegistry.ts | 150 +- packages/g-lite/src/css/parser/color.ts | 4 +- packages/g-lite/src/css/parser/points.ts | 9 +- .../properties/CSSPropertyLocalPosition.ts | 43 +- .../properties/CSSPropertyOffsetDistance.ts | 6 +- .../src/css/properties/CSSPropertyPath.ts | 22 +- .../src/css/properties/CSSPropertyPoints.ts | 17 - packages/g-lite/src/css/properties/index.ts | 1 - packages/g-lite/src/display-objects/Circle.ts | 7 +- .../src/display-objects/DisplayObject.ts | 36 +- .../g-lite/src/display-objects/Ellipse.ts | 2 +- packages/g-lite/src/display-objects/Line.ts | 16 +- packages/g-lite/src/display-objects/Path.ts | 26 +- .../g-lite/src/display-objects/Polygon.ts | 16 +- .../g-lite/src/display-objects/Polyline.ts | 4 +- packages/g-lite/src/dom/Element.ts | 7 +- packages/g-lite/src/global-runtime.ts | 3 +- .../g-lite/src/services/aabb/CircleUpdater.ts | 20 +- .../src/services/aabb/EllipseUpdater.ts | 20 +- .../g-lite/src/services/aabb/LineUpdater.ts | 8 +- .../g-lite/src/services/aabb/PathUpdater.ts | 10 +- .../src/services/aabb/PolylineUpdater.ts | 14 +- .../g-lite/src/services/aabb/RectUpdater.ts | 15 +- .../g-lite/src/services/aabb/TextUpdater.ts | 47 +- .../g-lite/src/services/aabb/interfaces.ts | 12 +- packages/g-lite/src/types.ts | 5 - packages/g-lite/src/utils/path.ts | 53 +- packages/g-lite/src/utils/transform-mat4.ts | 26 +- packages/g-plugin-box2d/src/Box2DPlugin.ts | 19 +- .../src/paths/Circle.ts | 4 +- .../src/paths/Ellipse.ts | 8 +- .../src/paths/Line.ts | 6 +- .../src/paths/Path.ts | 68 +- .../src/paths/Polygon.ts | 20 +- .../src/paths/Polyline.ts | 20 +- .../src/paths/Rect.ts | 35 +- .../src/CanvasPickerPlugin.ts | 8 +- packages/g-plugin-canvas-picker/src/Circle.ts | 4 +- .../g-plugin-canvas-picker/src/Ellipse.ts | 6 +- packages/g-plugin-canvas-picker/src/Image.ts | 11 +- packages/g-plugin-canvas-picker/src/Line.ts | 6 +- packages/g-plugin-canvas-picker/src/Path.ts | 10 +- .../g-plugin-canvas-picker/src/Polygon.ts | 8 +- .../g-plugin-canvas-picker/src/Polyline.ts | 6 +- packages/g-plugin-canvas-picker/src/Rect.ts | 16 +- .../src/shapes/styles/Image.ts | 4 +- .../src/shapes/styles/Text.ts | 12 +- .../src/CanvaskitRendererPlugin.ts | 40 +- .../src/renderers/Circle.ts | 24 +- .../src/renderers/Ellipse.ts | 28 +- .../src/renderers/Image.ts | 4 +- .../src/renderers/Line.ts | 18 +- .../src/renderers/Path.ts | 65 +- .../src/renderers/Polygon.ts | 4 +- .../src/renderers/Polyline.ts | 4 +- .../src/renderers/Rect.ts | 38 +- .../src/renderers/Text.ts | 4 +- packages/g-plugin-device-renderer/src/Mesh.ts | 7 +- .../src/MeshUpdater.ts | 17 +- .../src/TexturePool.ts | 7 +- .../src/drawcalls/Image.ts | 80 +- .../src/drawcalls/Instanced.ts | 71 +- .../src/drawcalls/InstancedFill.ts | 2 +- .../src/drawcalls/InstancedLine.ts | 36 +- .../src/drawcalls/InstancedPath.ts | 11 +- .../src/drawcalls/Mesh.ts | 2 + .../src/drawcalls/SDF.ts | 51 +- .../src/drawcalls/Text.ts | 46 +- .../src/lights/Light.ts | 2 +- .../src/shader/image.vert | 7 +- .../src/shader/line.vert | 8 +- .../src/shader/mesh.vert | 2 +- .../src/shader/sdf.vert | 7 +- .../src/shader/text.vert | 7 +- .../src/HTMLRenderingPlugin.ts | 14 +- .../g-plugin-matterjs/src/MatterJSPlugin.ts | 52 +- .../src/renderers/Circle.ts | 4 +- .../src/renderers/Ellipse.ts | 10 +- .../src/renderers/Line.ts | 10 +- .../src/renderers/Path.ts | 4 +- .../src/renderers/Polygon.ts | 4 +- .../src/renderers/Polyline.ts | 4 +- .../src/renderers/Rect.ts | 10 +- .../src/RoughElementLifeCycleContribution.ts | 57 +- .../src/SVGRendererPlugin.ts | 26 +- .../src/shapes/paths/Image.ts | 11 +- .../src/shapes/paths/Line.ts | 10 +- .../src/shapes/paths/Path.ts | 13 +- .../src/shapes/paths/Polyline.ts | 4 +- .../src/shapes/paths/Rect.ts | 11 +- .../src/shapes/paths/Text.ts | 10 +- packages/g-shader-components/billboard.vert | 4 +- pnpm-lock.yaml | 41 +- 122 files changed, 3649 insertions(+), 984 deletions(-) create mode 100644 __tests__/demos/2d/clippath.ts create mode 100644 __tests__/demos/2d/gradient.ts create mode 100644 __tests__/demos/2d/pattern.ts create mode 100644 __tests__/demos/2d/transform.ts create mode 100644 __tests__/demos/3d/force.ts create mode 100644 __tests__/demos/animation/line-dash.ts create mode 100644 __tests__/demos/animation/marching-ants.ts create mode 100644 __tests__/demos/animation/multiple-animations-per-element.ts create mode 100644 __tests__/demos/animation/offset-path.ts create mode 100644 __tests__/demos/bugfix/1624.ts create mode 100644 __tests__/demos/event/circle.ts create mode 100644 __tests__/demos/event/ellipse.ts rename __tests__/demos/{2d => event}/image-non-transparent-pixel.ts (90%) create mode 100644 __tests__/demos/event/index.ts create mode 100644 __tests__/demos/perf/circles.ts create mode 100644 __tests__/demos/perf/index.ts diff --git a/__tests__/demos/2d/circle.ts b/__tests__/demos/2d/circle.ts index 250cec31a..7322b41b7 100644 --- a/__tests__/demos/2d/circle.ts +++ b/__tests__/demos/2d/circle.ts @@ -17,41 +17,43 @@ export async function circle(context) { const circle2 = circle1.cloneNode(); circle2.style.stroke = 'green'; circle2.style.lineWidth = '2px'; - circle2.setPosition(30, 10); + circle2.style.transform = 'translate(20px, 0)'; canvas.appendChild(circle2); // transparent const circle3 = circle2.cloneNode(); circle3.style.fill = 'transparent'; - circle3.setPosition(50, 10); + circle3.setPosition(40, 0); canvas.appendChild(circle3); // none fill const circle4 = circle2.cloneNode(); circle4.style.fill = 'none'; - circle4.setPosition(70, 10); + circle4.setPosition(60, 0); canvas.appendChild(circle4); // dashed const circle5 = circle2.cloneNode(); circle5.style.lineDash = [2, 2]; - circle5.setPosition(90, 10); + circle5.setPosition(80, 0); canvas.appendChild(circle5); // dashed with offset const circle6 = circle2.cloneNode(); circle6.style.lineDash = [2, 2]; circle6.style.lineDashOffset = 2; - circle6.setPosition(110, 10); + circle6.setPosition(100, 0); canvas.appendChild(circle6); const circle7 = circle1.cloneNode(); circle7.style.opacity = 0.5; - circle7.setPosition(130, 10); + circle7.setPosition(120, 0); canvas.appendChild(circle7); // with shadow const circle8 = circle1.cloneNode(); + circle8.style.cx = 0; + circle8.style.cy = 0; circle8.style.r = 20; circle8.style.shadowBlur = 10; circle8.style.shadowColor = 'blue'; @@ -60,19 +62,22 @@ export async function circle(context) { // with gradient const circle9 = circle1.cloneNode(); + circle9.style.cx = 20; + circle9.style.cy = 20; circle9.style.r = 20; circle9.style.fill = 'l(0) 0:#ffffff 0.5:#7ec2f3 1:#1890ff'; - circle9.setPosition(60, 60); + circle9.setPosition(40, 40); canvas.appendChild(circle9); const circle10 = circle1.cloneNode(); + circle10.style.cx = 20; + circle10.style.cy = 20; circle10.style.r = 20; circle10.style.fill = 'r(0.5, 0.5, 1) 0:#ffffff 1:#1890ff'; - circle10.setPosition(100, 60); + circle10.setPosition(80, 40); canvas.appendChild(circle10); // transform const circle11 = circle1.cloneNode(); - circle11.scaleLocal(2); - circle11.setPosition(140, 60); + circle11.style.transform = 'scale(2) translate(130, 50)'; canvas.appendChild(circle11); } diff --git a/__tests__/demos/2d/clippath.ts b/__tests__/demos/2d/clippath.ts new file mode 100644 index 000000000..b463212a9 --- /dev/null +++ b/__tests__/demos/2d/clippath.ts @@ -0,0 +1,82 @@ +import { Circle, Rect, Path, Group } from '../../../packages/g'; + +export async function clipPath(context) { + const { canvas } = context; + await canvas.ready; + + // in user space + const clipPathCircle = new Circle({ + style: { + cx: 150, + cy: 150, + r: 35, + fill: 'blue', + }, + }); + + const rect1 = new Rect({ + style: { + x: 0, + y: 0, + width: 45, + height: 45, + stroke: 'white', + strokeWidth: 2, + fill: 'red', + clipPath: clipPathCircle, + cursor: 'pointer', + // transform: 'translate(200px, 200px)', + }, + }); + const rect2 = rect1.cloneNode(); + rect2.style.y = 55; + const rect3 = rect1.cloneNode(); + rect3.style.x = 55; + rect3.style.y = 55; + const rect4 = rect1.cloneNode(); + rect4.style.x = 55; + rect4.style.y = 0; + + const clipPathRect = new Rect({ + style: { + x: 125, + y: 125, + width: 50, + height: 50, + }, + }); + const clipPath = new Path({ + style: { + stroke: 'black', + lineWidth: 2, + path: 'M 10,10 L -10,0 L 10,-10 Z', + anchor: [0.5, 0.5], + }, + }); + + const g = new Group(); + const group = new Group({ + style: { + transform: 'translate(100, 100)', + }, + }); + g.appendChild(clipPathCircle); + group.appendChild(rect1); + group.appendChild(rect2); + group.appendChild(rect3); + group.appendChild(rect4); + g.appendChild(group); + + canvas.appendChild(g); + + // g.style.x = 200; + // g.style.y = 200; + + clipPathCircle.animate( + [{ transform: 'scale(1)' }, { transform: 'scale(2)' }], + { + duration: 1500, + iterations: Infinity, + }, + ); +} diff --git a/__tests__/demos/2d/ellipse.ts b/__tests__/demos/2d/ellipse.ts index 2d5b13ef4..2558fe8af 100644 --- a/__tests__/demos/2d/ellipse.ts +++ b/__tests__/demos/2d/ellipse.ts @@ -18,60 +18,62 @@ export async function ellipse(context) { const ellipse2 = ellipse1.cloneNode(); ellipse2.style.stroke = 'green'; ellipse2.style.lineWidth = '2px'; - ellipse2.setPosition(40, 20); + ellipse2.setPosition(20, 0); canvas.appendChild(ellipse2); // transparent const ellipse3 = ellipse2.cloneNode(); ellipse3.style.fill = 'transparent'; - ellipse3.setPosition(60, 20); + ellipse3.setPosition(40, 0); canvas.appendChild(ellipse3); // none fill const ellipse4 = ellipse2.cloneNode(); ellipse4.style.fill = 'none'; - ellipse4.setPosition(80, 20); + ellipse4.setPosition(60, 0); canvas.appendChild(ellipse4); // dashed const ellipse5 = ellipse2.cloneNode(); ellipse5.style.lineDash = [2, 2]; - ellipse5.setPosition(100, 20); + ellipse5.setPosition(80, 0); canvas.appendChild(ellipse5); // dashed with offset const ellipse6 = ellipse2.cloneNode(); ellipse6.style.lineDash = [2, 2]; ellipse6.style.lineDashOffset = 2; - ellipse6.setPosition(120, 20); + ellipse6.setPosition(100, 0); canvas.appendChild(ellipse6); const ellipse7 = ellipse1.cloneNode(); ellipse7.style.opacity = 0.5; - ellipse7.setPosition(140, 20); + ellipse7.setPosition(120, 0); canvas.appendChild(ellipse7); // with shadow const ellipse8 = ellipse1.cloneNode(); ellipse8.style.shadowBlur = 10; ellipse8.style.shadowColor = 'blue'; - ellipse8.setPosition(20, 60); + ellipse8.setPosition(0, 40); canvas.appendChild(ellipse8); // with gradient const ellipse9 = ellipse1.cloneNode(); ellipse9.style.fill = 'l(0) 0:#ffffff 0.5:#7ec2f3 1:#1890ff'; - ellipse9.setPosition(60, 60); + ellipse9.setPosition(40, 40); canvas.appendChild(ellipse9); const ellipse10 = ellipse1.cloneNode(); ellipse10.style.fill = 'r(0.5, 0.5, 1) 0:#ffffff 1:#1890ff'; - ellipse10.setPosition(100, 60); + ellipse10.setPosition(80, 40); canvas.appendChild(ellipse10); // transform const ellipse11 = ellipse1.cloneNode(); - ellipse11.scaleLocal(2); - ellipse11.setPosition(140, 100); + ellipse11.style.cx = 0; + ellipse11.style.cy = 0; + ellipse11.style.transformOrigin = '0 0'; + ellipse11.style.transform = 'scale(2) translate(140, 100)'; canvas.appendChild(ellipse11); } diff --git a/__tests__/demos/2d/gradient.ts b/__tests__/demos/2d/gradient.ts new file mode 100644 index 000000000..d4330dec6 --- /dev/null +++ b/__tests__/demos/2d/gradient.ts @@ -0,0 +1,125 @@ +import { Rect, HTML, Line } from '../../../packages/g'; + +export async function gradient(context) { + const { canvas } = context; + await canvas.ready; + // single linear gradient + const rect1 = new Rect({ + style: { + x: 50, + y: 50, + width: 200, + height: 100, + fill: 'linear-gradient(0deg, blue, green 40%, red)', + }, + }); + + // multi linear gradients + const rect2 = new Rect({ + style: { + x: 50, + y: 250, + width: 200, + height: 100, + fill: `linear-gradient(217deg, rgba(255,0,0,.8), rgba(255,0,0,0) 70.71%), + linear-gradient(127deg, rgba(0,255,0,.8), rgba(0,255,0,0) 70.71%), + linear-gradient(336deg, rgba(0,0,255,.8), rgba(0,0,255,0) 70.71%)`, + }, + }); + + // single radial gradient + const rect3 = new Rect({ + style: { + x: 350, + y: 50, + width: 200, + height: 100, + fill: 'radial-gradient(circle at center, red, blue, green 100%)', + }, + }); + + // hard stop + const rect4 = new Rect({ + style: { + x: 350, + y: 250, + width: 200, + height: 100, + fill: 'radial-gradient(red 50%, blue 50%)', + }, + }); + + const line1 = new Line({ + style: { + x1: 50, + y1: 180, + x2: 250, + y2: 180, + strokeWidth: 10, + stroke: 'linear-gradient(0deg, blue, green 40%, red)', + }, + }); + const line2 = new Line({ + style: { + x1: 350, + y1: 180, + x2: 550, + y2: 180, + strokeWidth: 10, + stroke: 'radial-gradient(circle at center, red, blue, green 100%)', + }, + }); + + canvas.appendChild(line1); + canvas.appendChild(line2); + + canvas.appendChild(rect1); + canvas.appendChild(rect2); + canvas.appendChild(rect3); + canvas.appendChild(rect4); + + canvas.appendChild( + new HTML({ + style: { + x: 100, + y: 20, + height: 30, + width: 200, + innerHTML: 'linear gradient', + }, + }), + ); + canvas.appendChild( + new HTML({ + style: { + x: 60, + y: 220, + height: 30, + width: 200, + innerHTML: 'multiple linear gradients', + }, + }), + ); + canvas.appendChild( + new HTML({ + style: { + x: 350, + y: 20, + height: 30, + width: 200, + innerHTML: 'radial gradient', + }, + }), + ); + canvas.appendChild( + new HTML({ + style: { + x: 350, + y: 220, + height: 30, + width: 200, + innerHTML: 'hard color stop', + }, + }), + ); +} diff --git a/__tests__/demos/2d/image.ts b/__tests__/demos/2d/image.ts index 316811730..482dffe32 100644 --- a/__tests__/demos/2d/image.ts +++ b/__tests__/demos/2d/image.ts @@ -6,12 +6,25 @@ export async function image(context) { const image1 = new Image({ style: { - x: 200, - y: 100, + x: 0, + y: 0, width: 200, height: 200, img: 'https://gw.alipayobjects.com/mdn/rms_6ae20b/afts/img/A*N4ZMS7gHsUIAAAAAAAAAAABkARQnAQ', }, }); canvas.appendChild(image1); + + // Use `keepAspectRatio` so that the image will not be stretched + const image2 = new Image({ + style: { + x: 200, + y: 100, + width: 100, + keepAspectRatio: true, + img: 'https://gw.alipayobjects.com/mdn/rms_6ae20b/afts/img/A*N4ZMS7gHsUIAAAAAAAAAAABkARQnAQ', + cursor: 'pointer', + }, + }); + canvas.appendChild(image2); } diff --git a/__tests__/demos/2d/index.ts b/__tests__/demos/2d/index.ts index 068d5ba71..bb4c627b8 100644 --- a/__tests__/demos/2d/index.ts +++ b/__tests__/demos/2d/index.ts @@ -2,9 +2,12 @@ export { circle } from './circle'; export { ellipse } from './ellipse'; export { rect } from './rect'; export { image } from './image'; -export { imageNonTransparentPixel } from './image-non-transparent-pixel'; export { line } from './line'; export { polyline } from './polyline'; export { polygon } from './polygon'; export { path } from './path'; export { text } from './text'; +export { gradient } from './gradient'; +export { transform } from './transform'; +export { clipPath } from './clippath'; +export { pattern } from './pattern'; diff --git a/__tests__/demos/2d/path.ts b/__tests__/demos/2d/path.ts index 7a9898b82..c22d2960a 100644 --- a/__tests__/demos/2d/path.ts +++ b/__tests__/demos/2d/path.ts @@ -62,7 +62,7 @@ export async function path(context) { }); canvas.appendChild(path2); path2.scale(0.2); - path2.translateLocal(-100, 20); + path2.translateLocal(0, 20); // Bezier const path3 = new Path({ diff --git a/__tests__/demos/2d/pattern.ts b/__tests__/demos/2d/pattern.ts new file mode 100644 index 000000000..6f7112b2c --- /dev/null +++ b/__tests__/demos/2d/pattern.ts @@ -0,0 +1,199 @@ +import { CanvasEvent, Circle, Rect, Group, HTML } from '../../../packages/g'; +import SimplexNoise from 'simplex-noise'; + +/** + * + * support the following image source: + * * HTMLImageElement () + * * HTMLCanvasElement () + * * HTMLVideoElement (