@strategicprojects/rpic 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +93 -10
- package/package.json +1 -1
- package/pkg/rpic_wasm_bg.wasm +0 -0
- package/pkg/rpic_wasm_math_bg.wasm +0 -0
package/index.js
CHANGED
|
@@ -101,6 +101,11 @@ export function renderSvg(src, opts) {
|
|
|
101
101
|
*/
|
|
102
102
|
export function animate(root, animations, gsap) {
|
|
103
103
|
const tl = gsap.timeline();
|
|
104
|
+
// GSAP's MotionPath/MorphSVG plugins need real <path> elements, but rpic
|
|
105
|
+
// emits <line>/<rect>/<circle>/<polygon>. Convert the shapes those effects
|
|
106
|
+
// reference to <path> up front — before any tween captures element refs, so
|
|
107
|
+
// a `draw` on the same shape traces the path instead of a detached primitive.
|
|
108
|
+
preconvertGeometry(root, animations);
|
|
104
109
|
for (const a of animations) {
|
|
105
110
|
const sel =
|
|
106
111
|
typeof CSS !== 'undefined' && CSS.escape ? '#' + CSS.escape(a.id) : `[id="${a.id}"]`;
|
|
@@ -173,14 +178,90 @@ function withOverrides(vars, a) {
|
|
|
173
178
|
return vars;
|
|
174
179
|
}
|
|
175
180
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
//
|
|
179
|
-
//
|
|
181
|
+
const SVG_NS = 'http://www.w3.org/2000/svg';
|
|
182
|
+
|
|
183
|
+
// Build SVG path data (`d`) for the primitives rpic emits. GSAP's MotionPath
|
|
184
|
+
// and MorphSVG need a real <path>; passing a raw <line>/<rect>/<circle> throws
|
|
185
|
+
// "Expecting a <path> element or an SVG path data string".
|
|
186
|
+
function pathDataOf(el) {
|
|
187
|
+
const n = el.tagName.toLowerCase();
|
|
188
|
+
const f = (a) => parseFloat(el.getAttribute(a)) || 0;
|
|
189
|
+
if (n === 'path') return el.getAttribute('d');
|
|
190
|
+
if (n === 'line') return `M${f('x1')},${f('y1')} L${f('x2')},${f('y2')}`;
|
|
191
|
+
if (n === 'polyline' || n === 'polygon') {
|
|
192
|
+
const nums = (el.getAttribute('points') || '').trim().split(/[\s,]+/).map(Number);
|
|
193
|
+
if (nums.length < 4) return null;
|
|
194
|
+
let d = '';
|
|
195
|
+
for (let i = 0; i + 1 < nums.length; i += 2) d += `${i ? 'L' : 'M'}${nums[i]},${nums[i + 1]} `;
|
|
196
|
+
return n === 'polygon' ? d + 'Z' : d.trim();
|
|
197
|
+
}
|
|
198
|
+
if (n === 'rect') return `M${f('x')},${f('y')} h${f('width')} v${f('height')} h${-f('width')} Z`;
|
|
199
|
+
if (n === 'circle') {
|
|
200
|
+
const r = f('r'), cx = f('cx'), cy = f('cy');
|
|
201
|
+
return `M${cx - r},${cy} a${r},${r} 0 1,0 ${2 * r},0 a${r},${r} 0 1,0 ${-2 * r},0 Z`;
|
|
202
|
+
}
|
|
203
|
+
if (n === 'ellipse') {
|
|
204
|
+
const rx = f('rx'), ry = f('ry'), cx = f('cx'), cy = f('cy');
|
|
205
|
+
return `M${cx - rx},${cy} a${rx},${ry} 0 1,0 ${2 * rx},0 a${rx},${ry} 0 1,0 ${-2 * rx},0 Z`;
|
|
206
|
+
}
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Replace a primitive SVG element with an equivalent <path> (preserving paint),
|
|
211
|
+
// in place. Idempotent: a <path> is returned untouched.
|
|
212
|
+
function convertToPath(el) {
|
|
213
|
+
if (!el || el.tagName.toLowerCase() === 'path') return el;
|
|
214
|
+
const d = pathDataOf(el);
|
|
215
|
+
if (!d || !el.parentNode || typeof document === 'undefined') return el;
|
|
216
|
+
const p = document.createElementNS(SVG_NS, 'path');
|
|
217
|
+
p.setAttribute('d', d);
|
|
218
|
+
for (const attr of el.attributes) {
|
|
219
|
+
if (!/^(x1|y1|x2|y2|points|x|y|width|height|cx|cy|r|rx|ry)$/.test(attr.name)) {
|
|
220
|
+
p.setAttribute(attr.name, attr.value);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
el.parentNode.replaceChild(p, el);
|
|
224
|
+
return p;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Convert every shape a `move`/`morph` references to a <path> before any tween
|
|
228
|
+
// captures element references (so a concurrent `draw` traces the path, not a
|
|
229
|
+
// now-detached primitive).
|
|
230
|
+
function preconvertGeometry(root, animations) {
|
|
231
|
+
const ALL = 'path, polyline, line, rect, circle, ellipse, polygon';
|
|
232
|
+
const pick = (id, sel) => {
|
|
233
|
+
if (!id) return;
|
|
234
|
+
const g = root.querySelector(`[id="${id}"]`);
|
|
235
|
+
const prim = g && g.querySelector(sel);
|
|
236
|
+
if (prim) convertToPath(prim);
|
|
237
|
+
};
|
|
238
|
+
for (const a of animations || []) {
|
|
239
|
+
if (a.effect === 'move') pick(a.path, 'path, polyline, line');
|
|
240
|
+
if (a.effect === 'morph') {
|
|
241
|
+
pick(a.id, ALL);
|
|
242
|
+
pick(a.morph, ALL);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// The `highlight` effect: emphasise `el`. With a target colour, recolour and
|
|
248
|
+
// thicken its outline AND give the whole object a small scale pulse, so the
|
|
249
|
+
// emphasis reads at a glance rather than a bare colour swap; without a colour,
|
|
250
|
+
// a colour-free scale pulse. One-directional `.to()` — pair with `repeat 1
|
|
251
|
+
// yoyo` for a flash-and-return, or `repeat -1 yoyo` for a continuous pulse.
|
|
180
252
|
function highlightWith(el, a, tl) {
|
|
181
253
|
if (a.color) {
|
|
182
254
|
const shapes = el.querySelectorAll('path, polyline, line, rect, circle, ellipse, polygon');
|
|
183
|
-
tl.to(
|
|
255
|
+
tl.to(
|
|
256
|
+
shapes,
|
|
257
|
+
withOverrides({ stroke: a.color, strokeWidth: '+=2', duration: a.duration, ease: 'power1.inOut' }, a),
|
|
258
|
+
a.start
|
|
259
|
+
);
|
|
260
|
+
tl.to(
|
|
261
|
+
el,
|
|
262
|
+
withOverrides({ scale: 1.1, transformOrigin: '50% 50%', duration: a.duration, ease: 'power1.inOut' }, a),
|
|
263
|
+
a.start
|
|
264
|
+
);
|
|
184
265
|
} else {
|
|
185
266
|
tl.to(
|
|
186
267
|
el,
|
|
@@ -193,15 +274,15 @@ function highlightWith(el, a, tl) {
|
|
|
193
274
|
// The `morph` effect: tween `el`'s outline into the shape of the object
|
|
194
275
|
// `a.morph` references, via GSAP's MorphSVGPlugin. The consumer must have
|
|
195
276
|
// registered it (`gsap.registerPlugin(MorphSVGPlugin)`); without it GSAP
|
|
196
|
-
// no-ops the morphSVG and the shape stays put.
|
|
277
|
+
// no-ops the morphSVG and the shape stays put. Both source and target were
|
|
278
|
+
// converted to <path> by preconvertGeometry.
|
|
197
279
|
function morphInto(root, el, a, tl) {
|
|
198
280
|
if (!a.morph) return;
|
|
199
281
|
const tsel =
|
|
200
282
|
typeof CSS !== 'undefined' && CSS.escape ? '#' + CSS.escape(a.morph) : `[id="${a.morph}"]`;
|
|
201
283
|
const target = root.querySelector(tsel);
|
|
202
|
-
const
|
|
203
|
-
const
|
|
204
|
-
const dst = target && target.querySelector(sel);
|
|
284
|
+
const src = el.querySelector('path');
|
|
285
|
+
const dst = target && target.querySelector('path');
|
|
205
286
|
if (!src || !dst) return;
|
|
206
287
|
tl.to(src, withOverrides({ morphSVG: dst, duration: a.duration, ease: 'power1.inOut' }, a), a.start);
|
|
207
288
|
}
|
|
@@ -215,7 +296,9 @@ function moveAlong(root, el, a, tl) {
|
|
|
215
296
|
const psel =
|
|
216
297
|
typeof CSS !== 'undefined' && CSS.escape ? '#' + CSS.escape(a.path) : `[id="${a.path}"]`;
|
|
217
298
|
const group = root.querySelector(psel);
|
|
218
|
-
|
|
299
|
+
// The shaft was converted to a <path> by preconvertGeometry; skip the
|
|
300
|
+
// arrowhead <polygon> — we want the line the traveller rides, not the tip.
|
|
301
|
+
const pathEl = group && group.querySelector('path');
|
|
219
302
|
if (!pathEl) return;
|
|
220
303
|
// `align` maps the path into the traveller's coordinate space; a `move`
|
|
221
304
|
// tween is a `.to()` (current position → along the path), not a `.from()`.
|
package/package.json
CHANGED
package/pkg/rpic_wasm_bg.wasm
CHANGED
|
Binary file
|
|
Binary file
|