Smoothly Morph SVG using snap.js - javascript

I tried using snap.js to achieve the morphing effect between my two blob shapes. However, the morphing transition is not as sooth as the example that I followed here. I created the shape in illustrator and exported it as svgs. Does anyone know what I can do to make the shape transition smoother without all the edges?
HTML
<svg id="blobs" width="1000x" height="1000px" style="display:block; xmlns="http://www.w3.org/2000/svg">
<path id="blob1" style="fill:#1B418C;" d="M97,183.67c-16.88-17.07-52.33,5.65-66.76-11.5-6.89-8.19-6.29-22.28-.78-30.66,7.24-11,22.89-13.06,37.67-12.77,55.35,1.09,88.12,1.66,114.19-1.28,22.39-2.52,44.1-5.24,66.17-21.08,18.16-13,24.94-26.61,35.13-39.6,29.43-37.52,70.95-49.29,102.08-58.12C474.6-16.84,586.8,17.4,601.17,66.78c.86,3,6.27,21.55-2.34,38.33-8.14,15.85-22.78,17-37.28,29.59-26.27,22.74-31.84,58-32.67,64.38-6,46.27,31,56.52,30.13,110.42-.13,8.17-.72,45-26.93,70.9-18.42,18.22-41,22.66-49.78,24.28-37.85,6.94-66.37-8.14-180.35-46.63-16.93-5.72-36.42-12.26-48-2.56-12.8,10.71-4.64,30.63-11.13,52.38-12.47,41.79-72.44,68.26-122.13,64.31C70.42,468.17,9.16,431.32,1,372.74-4,337.25,10.09,290.55,39,269.26c25.48-18.74,49.93-17.2,60.9-41.52C105.88,214.58,107.34,194.12,97,183.67Z"/>
<path opacity ="0" id="blob2" style="fill:#e29f27;" d="M339.37,14.68C393.67,53,361,146,405.32,167.41c27.67,13.41,44.91-20.72,105.38-17.23,13.63.79,76.17,4.41,102.16,51.2,1.08,1.94,20.54,38.24,5.72,73.58-14.52,34.64-44.42,26.47-74.46,65.27-29.78,38.48-16.82,67.74-45,84.67-20.37,12.22-48.3,9.65-67.5,1.29-45.57-19.82-43.69-73-98.9-92.4a107,107,0,0,0-18.13-4.55c-47-8.28-83.64,11.63-103.88,19.68-58,23.06-150,15.75-188-31.76-28.21-35.34-31.36-99-2.6-135,40.71-50.93,115-8.36,167.53-63.36,38.77-40.57,20.83-87.08,62.27-109.38C275.43-4.3,313.67-3.44,339.37,14.68Z"/>
</svg>
JAVASCRIPT:
var svg = document.getElementById("blobs");
var s = Snap(svg);
var Blob1 = Snap.select('#blob1');
var Blob2 = Snap.select('#blob2');
var Blob1Points = Blob1.node.getAttribute('d');
var Blob2Points = Blob2.node.getAttribute('d');
var toBlob2 = function(){
Blob1.animate({ d: Blob2Points }, 2000, mina.getById(blob1), toBlob1);}
var toBlob1 = function(){
Blob1.animate({ d: Blob1Points }, 2000, mina.getById(blob2), toBlob2);}
toBlob1();
My code
Any help would be greatly appreciated,
Thank You.

Related

Smoothly moving SVG element on event from current position to a given point

I'd like to move an SVG element (image) slowly from its current position (wherever it is) to a given point when an event is handled.
Since I don't know what the position is, I cannot prepare CSS animation based on keyframes.
So I tried doing the task via DOM manipulating:
var curX = parseFloat(image.getAttribute('x'));
var curY = parseFloat(image.getAttribute('y')); // Not used yet.
var curOpacity = parseFloat(image.getAttribute('opacity')); // Not used yet.
var animation = document.createElementNS(svgNS, 'animate');
animation.setAttribute('attributeName', 'x');
animation.setAttribute('from', curX);
animation.setAttribute('to', 0); // Move the image to the left border.
animation.setAttribute('dur', '2s'); // '3s', '5s'
animation.setAttribute('repeatCount', 1);
image.appendChild(animation);
If I set duration to 3s or 5s, it starts moving as soon as the animate element is appended, but when the animation is over the image is returned to the original position. (As I understand this is by design, but I'd like the image to keep its new position).
The second problem is when I set duration to 2s or less, the image stops moving smoothly, it just flickers to the left border and back (I don't know why).
Maybe, there is a better approach?
UPDATE
As it's answered below, the image keeps its position if fill is set to freeze. To reproduce the second problem it is enough to set timeout:
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" class="inline-block mb-1">
<image href="https://yari-demos.prod.mdn.mozit.cloud/en-US/docs/Web/SVG/Element/image/mdn_logo_only_color.png"
height="200" width="200" x="200" y="200"/>
<script>
// <![CDATA[
var startAnimation = function ()
{
const svgNS = "http://www.w3.org/2000/svg";
var image = document.querySelector('image');
var curX = parseFloat(image.getAttribute('x'));
var curY = parseFloat(image.getAttribute('y')); // Not used yet.
var curOpacity = parseFloat(image.getAttribute('opacity')); // Not used yet.
var animation = document.createElementNS(svgNS, 'animate');
animation.setAttribute('attributeName', 'x');
animation.setAttribute('from', curX);
animation.setAttribute('to', 0); // Move the image to the left border.
animation.setAttribute('dur', '2s'); // '3s', '5s'
animation.setAttribute('repeatCount', 1);
animation.setAttribute('fill', 'freeze');
image.appendChild(animation);
};
document.addEventListener('DOMContentLoaded', () => window.setTimeout(startAnimation, 1000));
// ]]>
</script>
</svg>
The image initially twitches and starts moving smoothly only after a half of the way. Chrome 102, x64. As for my computer performance, an animation based on window.requestAnimationFrame works with no glitches.
Set the 'fill' attribute of your animation:
animation.setAttribute('fill', 'freeze');
See here (MDN docs) for more details.
As for the jumpy animation behaviour, it hasn't happened in my tests, neither in the live example below nor on a standalone file.
Live example
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" class="inline-block mb-1">
<image href="https://yari-demos.prod.mdn.mozit.cloud/en-US/docs/Web/SVG/Element/image/mdn_logo_only_color.png" height="200" width="200" x="200" y="200"/>
<script>
// <![CDATA[
document.addEventListener ( 'DOMContentLoaded', function () {
const svgNS = "http://www.w3.org/2000/svg";
var image = document.querySelector('image');
var curX = parseFloat(image.getAttribute('x'));
var curY = parseFloat(image.getAttribute('y')); // Not used yet.
var curOpacity = parseFloat(image.getAttribute('opacity')); // Not used yet.
var animation = document.createElementNS(svgNS, 'animate');
animation.setAttribute('attributeName', 'x');
animation.setAttribute('from', curX);
animation.setAttribute('to', 0); // Move the image to the left border.
animation.setAttribute('dur', '2s'); // '3s', '5s'
animation.setAttribute('repeatCount', 1);
animation.setAttribute('fill', 'freeze');
image.appendChild(animation);
});
// ]]>
</script>
</svg>
UPDATE
The OP's bumpy animation problem when starting the animation through a timeout callback can be remedied by setting the animation element's begin attribute to the timeout delay:
animation.setAttribute('begin', '1s');

Appending a div containing canvas is not working

I am making use of canvg to convert svg present in div into canvas(upto this it's working fine) and then copying the innerHTML of div to another div, but it's not working. Canvas is coming but nothing will be present in that canvas.
Thanks in advance
<div id="k">
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
Sorry, your browser does not support inline SVG.
</svg>
</div>
<div id="kk">
<p>Watch me</p>
</div>
var svgTag = document.querySelectorAll('#k svg');
svgTag = svgTag[0];
var c = document.createElement('canvas');
c.width = svgTag.clientWidth;
c.height = svgTag.clientHeight;
svgTag.parentNode.insertBefore(c, svgTag);
svgTag.parentNode.removeChild(svgTag);
var div = document.createElement('div');
div.appendChild(svgTag);
canvg(c, div.innerHTML);
setTimeout(function(){
var data = $("#k").html();
$("#kk").append($(''+data+''));
},5000);
JSFiddle
The content of a canvas element is held as binary data, much like the content of an image element. Canvas elements do not have an innerHTML text property that can be used to recreate the canvas.
The following example shows a method of cloning a canvas element using standard canvas 2D methods:
function canvasClone(c1) {
var c2 = document.createElement('canvas');
c2.width=c1.width;
c2.height=c1.height;
c2.getContext("2d").drawImage(c1, 0,0);
return c2;
}
You can demonstrate it by including the function in the fiddle and changing the timeout processing to:
setTimeout(function(){
kk.appendChild(canvasClone(c));
},5000);
Feel free to rewrite the timeout code in JQuery if you prefer.

Javascript to Move SVG path without JS library

I want to drag path on SVG canvas.
The requirement is not to use any JS library.
Any example will help me.
I know with raphael.js its very easy.
Thanks.
Here is an example in pure JS:
<svg id='mysvg' height="800" width="800">
<path id='mypath' fill="#4444ff" stroke="#000000" d="M100,300L400,300L100,0Z" transform=""></path>
</svg>
JS:
el=document.getElementById("mypath");
sv=document.getElementById("mysvg");
flag=false; //to check if the mouse is currently down?
sv.onmousedown=function(e){
flag=true;
x1=e.clientX;
y1=e.clientY;
var t=el.getAttribute('transform');
if(t){
var parts = /translate\(\s*([^\s,)]+)[ ,]([^\s,)]+)/.exec(t);
var firstX = parts[1], firstY = parts[2];
x1=x1-firstX*1;
y1=y1-firstY*1;
}
/* x1 and y1 now contain the previous position*/
};
sv.onmousemove=function(e){
if(flag){
x=e.clientX;
y=e.clientY;
t="translate("+(x-x1)+","+(y-y1)+")"
el.setAttribute('transform',t);
}
};
sv.onmouseup=function(){flag=false};
fiddle: http://jsfiddle.net/gF8Wd/2/

How to map polygon in JavaScript

Please can you help me? I´m looking for a way, how to map a polygon in canvas with image file (.png or .jpg) but I´m quite confused and I don´t know how to do it... Can you tell me or give a link to a tutorial how to map a polygons in canvas with images? Thank you very much
Ok, I think I understand your question now.
You want to draw text a curved path...
Good news/Bad news:
Canvas cannot do this directly.
However you can use Html SVG to do it.
You can use a free program like Inkscape to design your logo.
Then just save it in SVG format (myLogo.svg).
Then you can load it into canvas like this:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var img=new Image();
img.onload=function(){
ctx.drawImage(img,0,0);
}
img.src="http://mySite.com/myLogo.svg";
Here is a quick demo of the kind of SVG you can create to fit your needs.
And here is a Fiddle: http://jsfiddle.net/m1erickson/v4EX3/
<!doctype html>
<html>
<head>
<style>
body{ background-color: ivory; }
div{float:left;}
</style>
</head>
<body>
<div>
<svg viewBox = "0 0 200 120">
<defs>
<path id = "curvedPath" d = "M 10,90 Q 100,15 200,70 Q 340,140 400,30"/>
</defs>
<g fill = "white">
<use x = "0" y = "0" xlink:href = "#curvedPath" stroke = "black" stroke-width="40" fill = "none"/>
<text font-size = "20">
<textPath xlink:href = "#curvedPath">
Use SVG to put your text on a curved path like this!
</textPath>
</text>
</g>
</svg>
</div>
</body>
</html>

Create SVG anchor element programmatically?

How do I create an SVG anchor through JavaScript? Please see relevant section and an example from spec. How do I convert this example to JavaScript (basically, how to dynamically generate the container element a so that when I click the ellipse, it navigates away.
<?xml version="1.0"?>
<svg width="5cm" height="3cm" viewBox="0 0 5 3" version="1.2" baseProfile="tiny"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Example 17_01</title>
<desc>A simple link on an ellipse.</desc>
<rect x=".01" y=".01" width="4.98" height="2.98"
fill="none" stroke="blue" stroke-width=".03"/>
<a xlink:href="http://www.w3.org/">
<ellipse cx="2.5" cy="1.5" rx="2" ry="1"
fill="red" />
</a>
</svg>
This is just basic DOM:
var xlinkNS="http://www.w3.org/1999/xlink", svgNS="http://www.w3.org/2000/svg";
var a = document.createElementNS(svgNS, "a");
a.setAttributeNS(xlinkNS,"href","http://www.w3.org/");
var ellipse = document.createElementNS(svgNS, "ellipse");
ellipse.setAttributeNS(null,"cx","2.5");
ellipse.setAttributeNS(null,"cy","1.5");
ellipse.setAttributeNS(null,"rx","2");
ellipse.setAttributeNS(null,"ry","1");
ellipse.setAttributeNS(null,"fill","red");
a.appendChild(ellipse);
document.documentElement.appendChild(a);
Using my function below, it's as easy as this:
// Find the first SVG element
var svg = document.getElementsByTagName('svg')[0];
var a = createOn(svg,'a',{'xlink:href':'http://www.w3.org/'});
createOn(a,'ellipse',{cx:2.5,cy:1.5,rx:1,ry:1,fill:'red'});
function createOn(root,name,attrs,text){
var doc = root.ownerDocument,
svg = root.ownerSVGElement || root; // In case the root _is_ the <svg>
var svgNS = svg.getAttribute('xmlns');
var el = doc.createElementNS(svgNS,name);
for (var attr in attrs){
if (!attrs.hasOwnProperty(attr)) continue;
var parts = attr.split(':');
if (parts[1]) el.setAttributeNS(
svg.getAttribute('xmlns:'+parts[0]),parts[1],attrs[attr]
);
else el.setAttributeNS(null,attr,attrs[attr]);
}
if (text) el.appendChild(document.createTextNode(text));
return root.appendChild(el);
}
If you already have the ellipse and want to wrap it, then create the 'a' element and:
// Get a reference to the ellipse however you like
var ellipse = document.getElementsByTagName('ellipse')[0];
// Put the anchor node immediately preceding the ellipse
ellipse.parentNode.insertBefore(a,ellipse);
// Move the ellipse to be a child of the anchor
a.appendChild(ellipse);

Categories