Reputation: 11
first of all, Im new to this webpage and Im really sorry for any mistakes.
So, I needed to animate a png image of a Calligraphy text just like a handwriting effect. Recently I've watched a youtube video https://www.youtube.com/watch?v=wab7lQKHXL4, its about making a handwriting animation with strokes clipped to texts. So I was wondering, if I could clip the strokes to a png image and animate it just like the handwriting animation.
I tried to import the png image inside inkspace and drawn path on top of it, gave suitable stroke-width saved it as a svg file, and tried to animate it with stroke-dasharray, but its not working ,its not even showing up. although I can do the same with written texts just like in the video.
So is there any way ,I can achieve the same goal using a png image. thanks in advance, and sorry if I don't make any sense
Upvotes: 0
Views: 456
Reputation: 21173
Once you have a d-path
, you can offload the work to a Native Web Component <draw-path>
disclaimer: The last drawn path is from Jake Archibald his 2013 blog: Animated Line Drawing SVG
window.customElements.define("draw-path", class extends HTMLElement {
constructor() {
let template = (id) => document.getElementById(id).content.cloneNode(true);
super() // super sets and returns this scope
.attachShadow({mode: "open"}) // sets and returns this.shadowRoot
.append(template(this.nodeName));
this.line = this.shadowRoot.querySelector("#line");
this.line.setAttribute("d", this.getAttribute("d") || "m10 60c30-70 55-70 75 0s55 70 85 0");
this.line.setAttribute("stroke", this.getAttribute("stroke") || "black");
this.line.setAttribute("stroke-width", this.getAttribute("stroke-width") || "2");
this.pen = this.shadowRoot.querySelector("#pen");
this.onmouseover = (evt) => this.draw();
}
connectedCallback() {
this.shadowRoot.querySelector("svg").setAttribute("viewBox",this.getAttribute("viewBox")||"0 0 180 150");
this.draw();
}
showpen(state = true, scale) {
this.pen.style.display = state ? 'initial' : 'none';
}
draw() {
clearInterval(this.drawing);
this.showpen();
this.dashoffset = 1;
this.pathlength = this.line.getTotalLength();
this.drawing = setInterval(() => this.update(), 50);
}
update() {
this.dashoffset -= this.getAttribute("speed") || 0.02;
let {x,y} = this.line.getPointAtLength(this.pathlength - this.dashoffset * this.pathlength);
this.pen.setAttribute("transform", `translate(${x-2} ${y-2})`);
this.line.style.strokeDashoffset = this.dashoffset;
if (this.dashoffset <= 0) this.end();
}
end() {
clearInterval(this.drawing);
this.showpen(false);
//console.log("end",this.line);
clearTimeout(this.timeout);
this.timeout = setTimeout(()=>this.draw(),2000);
}
});
<draw-path viewBox="20 20 60 60" d='M 61.739,30.223 C 61.739,30.223 55.369,26.515 51.839,25.606 48.819,24.825 45.469,24.022 42.489,24.964 40.379,25.63 38.329,27.161 37.349,29.137 35.899,32.044 35.099,36.236 36.989,38.878 36.989,38.878 46.459,41.753 50.839,42.679 54.399,43.431 57.729,48.053 56.429,52.972 55.599,56.117 51.469,57.478 48.379,58.502 45.569,59.433 42.389,59.495 39.509,58.837 36.089,58.056 30.199,53.913 30.199,53.913'></draw-path>
<draw-path stroke='red' stroke-width='8' speed=".01"></draw-path>
<draw-path stroke="deeppink" viewBox="0 0 400 370" d="M11.6 269s-19.7-42.4 6.06-68.2 48.5-6.06 59.1 12.1l-3.03 28.8 209-227s45.5-21.2 60.6 1.52c15.2 22.7-3.03 47-3.03 47l-225 229s33.1-12 48.5 7.58c50 63.6-50 97-62.1 37.9"></draw-path>
<template id="DRAW-PATH">
<style>
:host { display: inline-block }
svg { width: 180px; height: 130px; background: beige }
</style>
<svg xmlns="http://www.w3.org/2000/svg">
<path id='line' pathlength='1' stroke-dasharray='1' stroke-dashoffse='1' fill='transparent'/>
<path id='pen' stroke='black' stroke-width='2' fill='gold' d='m12 19l7-7l3 3l-7 7l-3-3zm6-6l-2-8l-14-3l4 15l7 1l5-5zm-16-11l8 8m-1 1a2 2 0 104 0a2 2 0 10-4 0'/>
</svg>
</template>
Note:
Be aware M
or m
(moves) in paths create a new stroke, drawn at the same time, not sequentially.
So stroke-dash*
settings are applied concurrent.
That is why in all blogs you only see single stroke simple paths or polylines used.
Upvotes: 0
Reputation: 21821
The video you are linking is astonishing, if not crooked. The commentary points out it is done with a "custom" version of Inkscape, and I have to say there are some UI elements I have never seen. (Admittedly, I am on an older version from 2021, but the video is from 2017, when Inkscape oficially wasn't even on verson 1.0.) Maybe the specialists on https://graphicdesign.stackexchange.com/ can tell you more about this "custom" version?
The core action is selecting from the menu bar Object -> Set Clip. If this is the same as selecting Object -> Clip -> Set in my version (1.02), this cannot work. This action sets a clip-path
. Clip paths only clip to their fill area and have no stroke properties.
What you have to do instead is
none
and the stroke color to white (#ffffff
)Also, even after watching for multiple times, I am not sure on which element the onload
attribute is set. Setting it on the path element in the mask certainly won't work, there is no load
event happening there.
The logical choice would be to wait for the image loading.
The result should be something like this:
document.querySelector('image#letter').addEventListener('load', function () {
setTimeout(() => document.querySelector('#stroke .dashed').style.strokeDashoffset = 0, 200)
})
.dashed {
fill: none;
stroke: white;
stroke-width: 8px;
stroke-dasharray: 100;
stroke-dashoffset: 100;
transition: stroke-dashoffset 1s linear;
}
image#letter {
mask: url(#stroke);
}
<svg viewBox="0 0 100 100" width="100" height="100">
<mask id="stroke" maskUnits="userSpaceOnUse">
<path class="dashed" d="M 61.739,30.223 C 61.739,30.223 55.369,26.515 51.839,25.606 48.819,24.825 45.469,24.022 42.489,24.964 40.379,25.63 38.329,27.161 37.349,29.137 35.899,32.044 35.099,36.236 36.989,38.878 36.989,38.878 46.459,41.753 50.839,42.679 54.399,43.431 57.729,48.053 56.429,52.972 55.599,56.117 51.469,57.478 48.379,58.502 45.569,59.433 42.389,59.495 39.509,58.837 36.089,58.056 30.199,53.913 30.199,53.913" />
</mask>
<image id="letter" width="100" height="100"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAA
GXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAwxJREFUeJzt2zuIHWUYBuAnm2hi
NAnBoCJE8FIERTQRBRErgyIGSysRL6CtYqOgTUhh4xJILAUNYqFdai0sRBSUxAvEJsRLlIAXMOom
XlaL/wjx7Jxkz+ycmdlz3gcGln+Z4Z39ds4/881/iIiIiIiIiIiIiIiIiIiIiIgY19quA9SwFlux
afDz2W7jNGtN1wEuYB0ewD24HTcphTjXIk7iOL7ER4Pti8HvogHr8QK+wz81t1N4Dfe1G3363IrP
1S/E8PZeu/FXZl3XAYbsxLvKHHE+v+APbMBlkw41q7YqHzNV/+XH8Lwyj2wc2u9i7MDDOIivreIr
pE8OWlqIP/GM8a7kNUrhXsFpKUgtm3DG0oI8ssLjbsGeFR5jJj1klU/GTZnrOsDAbRVjb7eeogf6
UpCrKsaOt56iB/pSkKpb175ka1VfTvqnirEbW0/RA30pyImKscdxUcs5YuBO1Q+Eb+LSDnPNrDmj
+1ff4DnlaTxatEdpl5+vUXgSb+Ap7JKPtIl70Xid3DN4Hy/hfqVtHw17FL+p12r/AQdwfduhp93V
SnNwQb3CnMXLSns+GrQZj+EwfjZ+YT5U3QXorb6/Uz/XHG7GHcqEvkt5oXWhif1j3K1cbTFhG3Ev
XsWvRl8p+7sKOMuuwCHVBVlQ5qXowD7VRXm6y1CzbA6fWlqQw12GWq6+NBebtIjXK8ZvaDtIHdNY
EDhSMbat9RQ1TGtBfq8Y+6v1FDVMa0Gq7qhOtZ6ihmktyO6KsRNth1jNHlRWtjfhWmWB3PBd1pMN
HX8mzONvvKW0Q+q6UpnQq9r0q6qn1bV5//8DHsWzuGaZ+2/AE0Z/fWFfw3knpi/NxXllDW+Vb/EB
PsOPyjuPRWX56XbcoswZm0fsfwR3qb7zihGGr5Cmtk9weYvnMTV24x3lWaGJQixgLy5p8ySm0Tbl
hdQhfGX8QhxT3ssvd+7pnb7MIaNsV1Yw7sB1yjzx35c+Tw+275X55aiyZCgiIiIiIiIiIiIiIiIi
IiIiIiIiIiIiIiIiIiIiIiIm51/fJjIk1BlBegAAAABJRU5ErkJggg==
"
</svg>
Upvotes: 0