Home > Back-end >  svg handwriting effect ,Can I clip the stroke path to a png image instead of text?
svg handwriting effect ,Can I clip the stroke path to a png image instead of text?

Time:08-07

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

CodePudding user response:

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

  1. set the fill to none and the stroke color to white (#ffffff)
  2. select a Object -> Mask -> Set

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  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>

CodePudding user response:

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.

  • Related