diff --git a/.vscode/settings.json b/.vscode/settings.json index aeec8a2dc..7d129b634 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,14 @@ { "editor.formatOnSave": true, - "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"], + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact" + ], "typescript.tsdk": "node_modules/typescript/lib", "editor.codeActionsOnSave": { - "source.fixAll.eslint": true + "source.fixAll.eslint": "explicit" }, "svg.preview.background": "dark-transparent" } diff --git a/__tests__/demos/2d/circle.ts b/__tests__/demos/2d/circle.ts index 250cec31a..3cc270c47 100644 --- a/__tests__/demos/2d/circle.ts +++ b/__tests__/demos/2d/circle.ts @@ -16,42 +16,44 @@ export async function circle(context) { const circle2 = circle1.cloneNode(); circle2.style.stroke = 'green'; - circle2.style.lineWidth = '2px'; - circle2.setPosition(30, 10); + circle2.style.lineWidth = 2; + 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,23 @@ 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.transformOrigin = 'center'; + circle11.style.transform = 'translate(130, 50) scale(2)'; canvas.appendChild(circle11); } diff --git a/__tests__/demos/2d/circles.ts b/__tests__/demos/2d/circles.ts deleted file mode 100644 index b9b3c36a6..000000000 --- a/__tests__/demos/2d/circles.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Circle, runtime } from '../../../packages/g'; - -export async function circles(context) { - runtime.enableCSSParsing = false; - - const { canvas } = context; - await canvas.ready; - - for (let i = 0; i < 50000; i++) { - const circle = new Circle({ - style: { - cx: Math.random() * 500, - cy: Math.random() * 500, - r: 5, - fill: 'red', - stroke: 'blue', - }, - }); - canvas.appendChild(circle); - - circle.addEventListener('mouseenter', () => { - circle.style.fill = 'green'; - }); - circle.addEventListener('mouseleave', () => { - circle.style.fill = 'red'; - }); - } -} diff --git a/__tests__/demos/2d/clippath.ts b/__tests__/demos/2d/clippath.ts new file mode 100644 index 000000000..267aa5336 --- /dev/null +++ b/__tests__/demos/2d/clippath.ts @@ -0,0 +1,136 @@ +import { Circle, Rect, Path, Group } from '../../../packages/g'; +import { Sector } from '../../../packages/g-components'; + +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', + transformOrigin: 'center', + }, + }); + + const rect1 = new Rect({ + style: { + x: 0, + y: 0, + width: 45, + height: 45, + stroke: 'white', + lineWidth: 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, + d: 'M 10,10 L -10,0 L 10,-10 Z', + }, + }); + + 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); + + // clipPathCircle.animate( + // [{ transform: 'scale(1)' }, { transform: 'scale(2)' }], + // { + // duration: 1500, + // iterations: Infinity, + // }, + // ); + + { + const sector = new Sector({ + style: { + x: 350, + y: 100, + lineWidth: 1, + sr: 100, + startAngle: -90, + fill: 'yellow', + opacity: 0.5, + endAngle: -270, + }, + }); + const group = new Group({ + style: { + clipPath: sector, + }, + }); + const circle1 = new Circle({ + style: { + fill: 'red', + cx: 300, + cy: 100, + r: 20, + }, + }); + const circle2 = new Circle({ + style: { + fill: 'red', + cx: 350, + cy: 100, + r: 20, + }, + }); + canvas.appendChild(group); + group.appendChild(circle1); + group.appendChild(circle2); + canvas.appendChild(sector); + + // sector.animate( + // [ + // { + // endAngle: -90, + // }, + // { + // endAngle: 270, + // }, + // ], + // { + // duration: 1000, + // iterations: Infinity, + // fill: 'both', + // }, + // ); + } +} diff --git a/__tests__/demos/2d/custom-element.ts b/__tests__/demos/2d/custom-element.ts new file mode 100644 index 000000000..4924d2b92 --- /dev/null +++ b/__tests__/demos/2d/custom-element.ts @@ -0,0 +1,71 @@ +import { Path, Polyline, Line } from '../../../packages/g'; +import { Arrow } from '../../../packages/g-components'; + +export async function customElement(context) { + const { canvas } = context; + await canvas.ready; + + // create an arrow + const lineArrow = new Arrow({ + id: 'lineArrow', + style: { + body: new Line({ + style: { + x1: 200, + y1: 100, + x2: 0, + y2: 0, + }, + }), + startHead: true, + stroke: '#1890FF', + lineWidth: 10, + cursor: 'pointer', + increasedLineWidthForHitTesting: 40, + }, + }); + lineArrow.translate(200, 100); + + const polylineArrow = new Arrow({ + id: 'polylineArrow', + style: { + body: new Polyline({ + style: { + points: [ + [0, 0], + [50, 0], + [50, 50], + [100, 50], + [100, 100], + [150, 100], + ], + }, + }), + startHead: true, + stroke: '#1890FF', + lineWidth: 10, + cursor: 'pointer', + }, + }); + polylineArrow.translate(200, 200); + + const pathArrow = new Arrow({ + id: 'pathArrow', + style: { + body: new Path({ + style: { + d: 'M 100,300' + 'l 50,-25' + 'a25,25 -30 0,1 50,-80', + }, + }), + startHead: true, + stroke: '#1890FF', + lineWidth: 10, + cursor: 'pointer', + }, + }); + pathArrow.translate(100, 150); + + canvas.appendChild(lineArrow); + canvas.appendChild(polylineArrow); + canvas.appendChild(pathArrow); +} diff --git a/__tests__/demos/2d/ellipse.ts b/__tests__/demos/2d/ellipse.ts index 2d5b13ef4..6637d127c 100644 --- a/__tests__/demos/2d/ellipse.ts +++ b/__tests__/demos/2d/ellipse.ts @@ -17,61 +17,61 @@ export async function ellipse(context) { const ellipse2 = ellipse1.cloneNode(); ellipse2.style.stroke = 'green'; - ellipse2.style.lineWidth = '2px'; - ellipse2.setPosition(40, 20); + ellipse2.style.lineWidth = 2; + 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.transformOrigin = 'center'; + ellipse11.style.transform = 'translate(140, 100) scale(2)'; canvas.appendChild(ellipse11); } diff --git a/__tests__/demos/2d/get-point.ts b/__tests__/demos/2d/get-point.ts new file mode 100644 index 000000000..0812993c7 --- /dev/null +++ b/__tests__/demos/2d/get-point.ts @@ -0,0 +1,59 @@ +import { Path, Polyline, Circle } from '../../../packages/g'; + +export async function getPoint(context) { + const { canvas, gui } = context; + await canvas.ready; + + const path = new Path({ + style: { + lineWidth: 1, + stroke: '#54BECC', + d: 'M 0,40 C 5.5555555555555545,40,22.222222222222218,44.44444444444445,33.33333333333333,40 C 44.444444444444436,35.55555555555556,55.55555555555554,14.66666666666667,66.66666666666666,13.333333333333336 C 77.77777777777777,12.000000000000002,88.88888888888887,32,100,32 C 111.11111111111113,32,122.22222222222221,14.66666666666667,133.33333333333331,13.333333333333336 C 144.44444444444443,12.000000000000002,155.55555555555557,24,166.66666666666669,24 C 177.7777777777778,24,188.8888888888889,11.111111111111114,200,13.333333333333336 C 211.1111111111111,15.555555555555557,222.22222222222226,35.111111111111114,233.33333333333334,37.333333333333336 C 244.44444444444443,39.55555555555555,255.55555555555551,31.22222222222223,266.66666666666663,26.66666666666667 C 277.77777777777777,22.111111111111114,294.4444444444444,12.777777777777779,300,10', + transform: 'translate(100, 100)', + }, + }); + const pointInPath = new Circle({ + style: { + r: 10, + fill: 'red', + }, + }); + canvas.appendChild(path); + canvas.appendChild(pointInPath); + + const polyline = new Polyline({ + style: { + lineWidth: 1, + stroke: '#54BECC', + points: [ + [200, 200], + [200, 300], + [300, 300], + ], + }, + }); + const pointInPath2 = new Circle({ + style: { + r: 10, + fill: 'red', + }, + }); + canvas.appendChild(polyline); + canvas.appendChild(pointInPath2); + + const getPointFolder = gui.addFolder('getPoint'); + const getPointConfig = { + ratio: 0, + }; + getPointFolder.add(getPointConfig, 'ratio', 0, 1).onChange((ratio) => { + let point = path.getPoint(ratio); + if (point) { + pointInPath.setPosition(point.x, point.y); + } + + let point2 = polyline.getPoint(ratio); + if (point2) { + pointInPath2.setPosition(point2.x, point2.y); + } + }); +} diff --git a/__tests__/demos/2d/gradient.ts b/__tests__/demos/2d/gradient.ts new file mode 100644 index 000000000..dde079a9e --- /dev/null +++ b/__tests__/demos/2d/gradient.ts @@ -0,0 +1,129 @@ +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)', + // transform: 'translate(50, 50)', + }, + }); + + // 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%)`, + // transform: 'translate(50, 250)', + }, + }); + + // 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%)', + // transform: 'translate(350, 50)', + }, + }); + + // hard stop + const rect4 = new Rect({ + style: { + x: 350, + y: 250, + width: 200, + height: 100, + fill: 'radial-gradient(red 50%, blue 50%)', + // transform: 'translate(350, 250)', + }, + }); + + const line1 = new Line({ + style: { + x1: 50, + y1: 180, + x2: 250, + y2: 180, + lineWidth: 10, + stroke: 'linear-gradient(0deg, blue, green 40%, red)', + }, + }); + const line2 = new Line({ + style: { + x1: 350, + y1: 180, + x2: 550, + y2: 180, + lineWidth: 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/html.ts b/__tests__/demos/2d/html.ts new file mode 100644 index 000000000..84161a49a --- /dev/null +++ b/__tests__/demos/2d/html.ts @@ -0,0 +1,134 @@ +import { HTML, Line, Rect, Text, CustomElement } from '../../../packages/g'; + +export async function html(context) { + const { canvas } = context; + await canvas.ready; + + // create a line + const line = new Line({ + style: { + x1: 200, + y1: 100, + x2: 400, + y2: 100, + stroke: '#1890FF', + lineWidth: 2, + }, + }); + const p1 = new HTML({ + id: 'p1', + name: 'p1-name', + className: 'p1-classname', + style: { + x: 200, + y: 100, + width: 60, + height: 30, + innerHTML: 'p1', + }, + }); + const p2 = new HTML({ + id: 'p2', + name: 'p2-name', + className: 'p2-classname', + style: { + x: 400, + y: 100, + width: 60, + height: 30, + innerHTML: 'p2', + }, + }); + + const rect = new Rect({ + name: 'test-name', + style: { + x: 200, + y: 200, + width: 300, + height: 100, + fill: '#1890FF', + }, + }); + const text = new Text({ + style: { + x: 350, + y: 250, + text: 'Hover me!', + fontSize: 22, + fill: '#000', + textAlign: 'center', + textBaseline: 'middle', + }, + }); + rect.appendChild(text); + const tooltip = new HTML({ + style: { + x: 0, + y: 0, + innerHTML: 'Tooltip', + fill: 'white', + stroke: 'black', + lineWidth: 6, + width: 100, + height: 30, + pointerEvents: 'none', + visibility: 'hidden', + }, + }); + + canvas.appendChild(line); + canvas.appendChild(p1); + canvas.appendChild(p2); + canvas.appendChild(rect); + canvas.appendChild(tooltip); + + rect.addEventListener('mousemove', (e) => { + tooltip.setPosition(e.x, e.y); + tooltip.style.visibility = 'visible'; + + console.log('move', e.target); + }); + rect.addEventListener('mouseleave', (e) => { + tooltip.setPosition(0, 0); + tooltip.style.visibility = 'hidden'; + + console.log('leave', e.target); + }); + + class Custom extends CustomElement<{}> { + constructor(config) { + super({ + ...config, + type: 'custom', + }); + + const tooltip = new HTML({ + style: { + x: 0, + y: 0, + innerHTML: 'Tooltip', + fill: 'white', + stroke: 'black', + lineWidth: 6, + width: 100, + height: 30, + }, + }); + this.appendChild(tooltip); + this.appendChild( + new Rect({ + style: { width: 100, height: 100, x: 0, y: 40, fill: 'red' }, + }), + ); + } + + connectedCallback() {} + } + const customEl = new Custom({ + style: { + transform: 'translate(200, 330)', + }, + }); + canvas.appendChild(customEl); +} diff --git a/__tests__/demos/2d/image.ts b/__tests__/demos/2d/image.ts index 316811730..4ac828b53 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', + src: '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, + src: '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..0ae824579 100644 --- a/__tests__/demos/2d/index.ts +++ b/__tests__/demos/2d/index.ts @@ -2,9 +2,21 @@ 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 { html } from './html'; 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 { transformText } from './transform-text'; +export { transformOrigin } from './transform-origin'; +export { transformSkew } from './transform-skew'; +export { clipPath } from './clippath'; +export { customElement } from './custom-element'; +export { marker } from './marker'; +export { pattern } from './pattern'; +export { pattern2 } from './pattern2'; +export { zIndex } from './z-index'; +export { getPoint } from './get-point'; diff --git a/__tests__/demos/2d/marker.ts b/__tests__/demos/2d/marker.ts new file mode 100644 index 000000000..733190ff6 --- /dev/null +++ b/__tests__/demos/2d/marker.ts @@ -0,0 +1,168 @@ +import { Path, Circle, Line, Rect, Image } from '../../../packages/g'; + +export async function marker(context) { + const { canvas } = context; + await canvas.ready; + + /** + * Arrow with triangle marker + */ + const arrowMarker = new Path({ + style: { + d: 'M 10,10 L -10,0 L 10,-10 Z', + stroke: '#1890FF', + transformOrigin: 'center', + }, + }); + const handle1 = new Circle({ + id: 'handle1', + style: { + draggable: true, + cursor: 'move', + fill: '#DEE9FF', + stroke: '#5B8FF9', + r: 10, + cx: 100, + cy: 50, + }, + }); + const handle2 = handle1.cloneNode(); + handle2.id = 'handle2'; + handle2.style.cx = 300; + handle2.style.cy = 50; + const arrow1 = new Line({ + style: { + x1: 100, + y1: 50, + x2: 300, + y2: 50, + stroke: '#F6BD16', + lineWidth: 6, + markerEnd: arrowMarker, + // markerEndOffset: 28, + }, + }); + + // /** + // * Arrow with rect marker + // */ + // const rectMarker = new Rect({ + // style: { + // x: -10, + // y: -10, + // width: 20, + // height: 20, + // fill: '#F6BD16', + // transform: 'rotate(45deg)', + // transformOrigin: 'center', + // }, + // }); + // const handle3 = handle1.cloneNode(); + // handle3.id = 'handle3'; + // handle3.style.cx = 100; + // handle3.style.cy = 150; + // const handle4 = handle1.cloneNode(); + // handle4.id = 'handle4'; + // handle4.style.cx = 300; + // handle4.style.cy = 150; + // const arrow2 = new Line({ + // style: { + // x1: 100, + // y1: 150, + // x2: 300, + // y2: 150, + // stroke: '#F6BD16', + // lineWidth: 6, + // markerEnd: rectMarker, + // markerEndOffset: 28, + // }, + // }); + + // /** + // * Arrow with image marker + // */ + // const imageMarker = new Image({ + // style: { + // x: -25, + // y: -25, + // width: 50, + // height: 50, + // src: 'https://gw.alipayobjects.com/mdn/rms_6ae20b/afts/img/A*N4ZMS7gHsUIAAAAAAAAAAABkARQnAQ', + // transformOrigin: 'center', + // transform: 'rotate(90deg)', + // }, + // }); + // const handle5 = handle1.cloneNode(); + // handle5.id = 'handle5'; + // handle5.style.cx = 100; + // handle5.style.cy = 250; + // const handle6 = handle1.cloneNode(); + // handle6.id = 'handle6'; + // handle6.style.cx = 300; + // handle6.style.cy = 250; + // const arrow3 = new Line({ + // style: { + // x1: 100, + // y1: 250, + // x2: 300, + // y2: 250, + // stroke: '#F6BD16', + // lineWidth: 6, + // markerEnd: imageMarker, + // markerEndOffset: 40, + // }, + // }); + + canvas.appendChild(arrow1); + canvas.appendChild(handle1); + canvas.appendChild(handle2); + + // canvas.appendChild(arrow2); + // canvas.appendChild(handle3); + // canvas.appendChild(handle4); + + // canvas.appendChild(arrow3); + // canvas.appendChild(handle5); + // canvas.appendChild(handle6); + + let shiftX = 0; + let shiftY = 0; + function moveAt(target, canvasX, canvasY) { + const newPosX = canvasX - shiftX; + const newPosY = canvasY - shiftY; + target.style.cx = newPosX; + target.style.cy = newPosY; + + // re-define arrow + if (target.id === 'handle1') { + arrow1.style.x1 = newPosX; + arrow1.style.y1 = newPosY; + } else if (target.id === 'handle2') { + arrow1.style.x2 = newPosX; + arrow1.style.y2 = newPosY; + // } else if (target.id === 'handle3') { + // arrow2.style.x1 = newPosX; + // arrow2.style.y1 = newPosY; + // } else if (target.id === 'handle4') { + // arrow2.style.x2 = newPosX; + // arrow2.style.y2 = newPosY; + // } else if (target.id === 'handle5') { + // arrow3.style.x1 = newPosX; + // arrow3.style.y1 = newPosY; + // } else if (target.id === 'handle6') { + // arrow3.style.x2 = newPosX; + // arrow3.style.y2 = newPosY; + } + } + + canvas.addEventListener('dragstart', function (e) { + const { cx, cy } = e.target.style; + shiftX = e.canvasX - cx; + shiftY = e.canvasY - cy; + + moveAt(e.target, e.canvasX, e.canvasY); + }); + canvas.addEventListener('drag', function (e) { + moveAt(e.target, e.canvasX, e.canvasY); + }); +} diff --git a/__tests__/demos/2d/path.ts b/__tests__/demos/2d/path.ts index 7a9898b82..e01523608 100644 --- a/__tests__/demos/2d/path.ts +++ b/__tests__/demos/2d/path.ts @@ -1,4 +1,4 @@ -import { Path } from '../../../packages/g'; +import { Path, Rect, Group } from '../../../packages/g'; export async function path(context) { const { canvas } = context; @@ -6,7 +6,7 @@ export async function path(context) { const path = new Path({ style: { - path: 'M10,10 L30,30 L10, 30', + d: 'M10,10 L30,30 L10, 30', stroke: 'red', lineWidth: 6, }, @@ -42,7 +42,7 @@ export async function path(context) { const path2 = new Path({ style: { - path: + d: 'M 100,300' + 'l 50,-25' + 'a25,25 -30 0,1 50,-25' + @@ -58,20 +58,10 @@ export async function path(context) { lineWidth: 10, lineJoin: 'round', stroke: '#54BECC', + cursor: 'pointer', }, }); canvas.appendChild(path2); - path2.scale(0.2); - path2.translateLocal(-100, 20); - - // Bezier - const path3 = new Path({ - style: { - lineWidth: 1, - stroke: '#54BECC', - transform: 'scale(0.6) translate(0, 100px)', - path: 'M 0,40 C 5.5555555555555545,40,22.222222222222218,44.44444444444445,33.33333333333333,40 C 44.444444444444436,35.55555555555556,55.55555555555554,14.66666666666667,66.66666666666666,13.333333333333336 C 77.77777777777777,12.000000000000002,88.88888888888887,32,100,32 C 111.11111111111113,32,122.22222222222221,14.66666666666667,133.33333333333331,13.333333333333336 C 144.44444444444443,12.000000000000002,155.55555555555557,24,166.66666666666669,24 C 177.7777777777778,24,188.8888888888889,11.111111111111114,200,13.333333333333336 C 211.1111111111111,15.555555555555557,222.22222222222226,35.111111111111114,233.33333333333334,37.333333333333336 C 244.44444444444443,39.55555555555555,255.55555555555551,31.22222222222223,266.66666666666663,26.66666666666667 C 277.77777777777777,22.111111111111114,294.4444444444444,12.777777777777779,300,10', - }, - }); - canvas.appendChild(path3); + path2.scale(0.5); + path2.translateLocal(0, 20); } diff --git a/__tests__/demos/2d/pattern.ts b/__tests__/demos/2d/pattern.ts new file mode 100644 index 000000000..9153efe35 --- /dev/null +++ b/__tests__/demos/2d/pattern.ts @@ -0,0 +1,199 @@ +import { Rect, HTML } from '../../../packages/g'; +import SimplexNoise from 'simplex-noise'; + +/** + * + * support the following image source: + * * HTMLImageElement () + * * HTMLCanvasElement () + * * HTMLVideoElement (