Looking for ideas on how to animate what looks like a laser drawing out a word in a cursive font using SVG. The animation can be done with SMIL or JavaScript I don't care - though I think it would be easier with SMIL.
I am pretty sure if I could just get the letters represented as a path I could figure out how to animate a line from a fixed point to the word path - even if the path is non-continuous.
Any ideas?
EDIT
My demo was very basic, essentially I wrote animate functions for each letter and arranged their timing. Here is the letter X for example:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" viewBox="0 0 100 100" preserveAspectRatio="none">
<rect x="0" y="0" width="100%" height="100%" fill="black"/>
<path id="word" stroke="red" d="M10 10 L40 40 M40 10 L10 40" />
<line x1="10" y1="10" x2="25" y2="50" stroke="blue" stroke-width="0.5">
<animate attributeName="x1" begin="0s" dur="1s" values="10; 40;" />
<animate attributeName="y1" begin="0s" dur="1s" values="10; 40;" />
<animate attributeName="x1" begin="1s" dur="1s" values="40; 10;" />
<animate attributeName="y1" begin="1s" dur="1s" values="10; 40;" />
<set attributeName="visibility" to="hidden" begin="2s" />
</line>
</svg>
I am sure we can all agree that this is not an ideal long term solution... I thought it would be relatively easy to animate one end of a LINE along a path but I am having problems just getting the path...
Extract the paths from the glyphs in question, then apply a dash-array animation as seen in this example on each of the paths.
From a high level perspective, I would think you would want to do something like render the font to a canvas, then use the pixel information to generate the animation sequence. A simple algorithm could just trace from left to right, it would be a good deal harder to figure out a single stroke path, but that is doable as well.
You don't mention any idea of what platform or any time constraints, so its hard to get much closer than that.
One possibility... SVG Fonts are, I understand, stored as a sequence of SVG commands used to draw individual characters. The vector-based nature of drawing in SVG would seem like it would be amenable to 'tracing out' characters in realtime; you might be able to make a conversion utility to pre-convert SVG fonts to simple paths.
Related
Note: I can't use JavaScript, because this is for a CSS Zen Garden sort of challenge. Please do not suggest a JS library.
I have 2 ideas that I'm not making headway on:
Use a SVG filter to just pixelate the dang image; I've been playing with <feMorphology operator="erode"/> and punching the contrast up after, but it looks bad.
Filter the image to be smaller, then scale it up using CSS and image-rendering to be all blocky. The hard part is Step A; I can't find any filter operations that scale the input.
Am I missing something? How can I get a "pixelated" effect using an SVG filter?
You can pixelate images if you have the right "magic" displacementMap. Feel free to use the one referenced below (courtesy of Zoltan Fegyver).
Update: Changed the sample code to inline the displacementmap image as a data: URI (thanks for the code IllidanS4.)
The original answer had the displacementMap image hosted on a different domain. This used to work - but browsers implemented the new Filters security measures that disallow this. For production code today, you need the displacement map image served from the same domain as the source graphic's file or you need to inline the displacementMap.
Update 2:
You may have to tweak the size of feImage and feGaussianBlur to avoid bugs in feTile that adds artifacts. For example - this seems to work better:
<feGaussianBlur stdDeviation="8" in="SourceGraphic" result="smoothed" />
<feImage width="15.4" height="15.4"
<svg x="0px" y="0px" width="810px" height="600px" viewBox="0 0 810 600" color-interpolation-filters="sRGB">
<defs>
<filter id="pixelate" x="0%" y="0%" width="100%" height="100%">
<!--Thanks to Zoltan Fegyver for figuring out pixelation and producing the awesome pixelation map. -->
<feGaussianBlur stdDeviation="2" in="SourceGraphic" result="smoothed" />
<feImage width="15" height="15" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWSURBVAgdY1ywgOEDAwKxgJhIgFQ+AP/vCNK2s+8LAAAAAElFTkSuQmCC" result="displacement-map" />
<feTile in="displacement-map" result="pixelate-map" />
<feDisplacementMap in="smoothed" in2="pixelate-map" xChannelSelector="R" yChannelSelector="G" scale="50" result="pre-final"/>
<feComposite operator="in" in2="SourceGraphic"/>
</filter>
</defs>
<image filter="url(#pixelate)" width="810" height="600" preserveAspectRatio="xMidYMid meet" xlink:href="http://uploads2.wikiart.org/images/vincent-van-gogh/the-starry-night-1889(1).jpg"/>
</svg>
The filter in Michael Mullany's answer didn't work for me, instead I found this filter by Taylor Hunt:
<svg>
<filter id="pixelate" x="0" y="0">
<feFlood x="4" y="4" height="2" width="2"/>
<feComposite width="10" height="10"/>
<feTile result="a"/>
<feComposite in="SourceGraphic" in2="a" operator="in"/>
<feMorphology operator="dilate" radius="5"/>
</filter>
</svg>
(use it in the same way as the other filter: By giving an image the attribute filter="url(#pixelate)")
In action in this CodePen: https://codepen.io/tigt/pen/aZYqrg
However, both these filters seem unable to handle SVGs where the drawing doesn't take up the entire viewBox.
Essentially I needed to make the center "cut-out" keep a fixed shape and size regardless of vector scale. Is there a way to achieve this?
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" preserveAspectRatio="none" style="fill-rule:evenodd;" viewBox="0 0 2802 2657">
<path d="M290 4c-95,733 -191,1466 -286,2200 760,150 1520,300 2279,450 172,-223 343,-446 515,-669 -114,-572 -229,-1144 -343,-1716 -722,-88 -1444,-176 -2165,-264zm696 1027c-103,111 -205,222 -308,333 94,111 188,222 282,333 342,-205 684,-410 1026,-616 -333,-17 -667,-34 -1000,-51z"/>
</svg>
So I managed to do something after some editing to your SVG.
To achieve what you're asking you'll need to use / have :
- the SVG mask attribute
- A very large shape for the mask ( as much large as the max scale you want to use on the visible shape )
- The shape that you want to resize
- Resize the shape with transforms
Your SVG should looks like the following
<svg>
<defs>
<mask id="theMask">
<path fill="#ffffff" d=""/>
</mask>
</defs>
<g mask="url(#theMask)">
<path fill="#ffffff" id="shapetoresize" d=""/>
</g>
</svg>
I posted a pen as a "Proof of concept"
Feel free to fork it and use it to achieve what you're trying to do.
Codepen
note: as pointed out by #thioutp , The JS is only for demo purposes, you don't need GSAP to achieve this.
I have a situation where there are multiple SVG elements in an HTML document. The elements are laid out using HTML rules, box model, flexbox, and now I'm told grid will start being used soon. This jsfiddle is an example, showing a pair of filled parabolas drawn with common endpoints and slightly different control points using a Quadratic path operation. Other cases may be simple horizontal or diagonal lines of various thickness.
In the first SVG in the fiddle, the two endpoints are in the same SVG block, and dimensions (in SVG units) are well known, and it is straightforward to draw the parabola. (the control points are also shown here)
However, the next two SVG demonstrate the problem. The endpoints are in separate SVGs, separated by unknown amounts of "stuff" here represented by a bit of text. It is clear that javascript will be required to rewrite the coordinates of the endpoints and control points, so that the parabola (or whatever) can connect the two.
How do I obtain the SVG unit coordinates of the endpoint in the second SVG relative to the coordinate system of the third SVG, so that I can connect the parabola to its left endpoint?
One possibly simplifying assumption can be made: the SVG unit to pixel coordinate ratio will be consistent for any drawing, although for added complexity that ratio may change from time to time (triggering the need to recalculate and re-draw the cross-SVG items).
A possibly complicating issue is that most of the endpoints will be nested in two layers of nested SVG elements: the outer SVG will have its SVG unit be the same size as the CSS pixel, but the inner SVG will have this different unit size that may change from time to time.
IDs or CLASSes can be added as necessary to the solution.
/**CSS*/
svg { overflow: visible; }
<!--HTML -->
<svg width="12cm" height="6cm" viewBox="0 0 1200 600"
xmlns="http://www.w3.org/2000/svg" version="1.1">
<title>Example quad01 - quadratic Bézier commands in path data</title>
<desc>Picture showing a "Q" a "T" command,
along with annotations showing the control points
and end points</desc>
<rect x="1" y="1" width="1198" height="598"
fill="none" stroke="blue" stroke-width="1" />
<path d="M500,300 Q300,50 100,300 Q300,75 500,300"
fill="green" stroke="green" stroke-width="1" />
<!-- End points -->
<g fill="black" >
<circle cx="100" cy="300" r="3"/>
<circle cx="500" cy="300" r="3"/>
</g>
<!-- Control points and lines from end points to control points -->
<g fill="#888888" >
<circle cx="300" cy="50" r="3"/>
<circle cx="300" cy="75" r="3"/>
</g>
<path d="M100,300 L300,50 L500,300
L300,75 L100,300"
fill="none" stroke="#888888" stroke-width="1" />
</svg>
<br>
<svg width="2cm" height="6cm" viewBox="0 0 200 600"
xmlns="http://www.w3.org/2000/svg" version="1.1">
<title>Example quad01 - quadratic Bézier commands in path data</title>
<desc>SVG left endpoint</desc>
<rect x="1" y="1" width="198" height="598"
fill="none" stroke="blue" stroke-width="1" />
<!-- End points -->
<g fill="black" >
<circle cx="100" cy="300" r="3"/>
</g>
</svg>
some stuff here
<svg width="2cm" height="6cm" viewBox="0 0 200 600"
xmlns="http://www.w3.org/2000/svg" version="1.1">
<title>Example quad01 - quadratic Bézier commands in path data</title>
<desc>SVG right endpoint</desc>
<rect x="1" y="1" width="198" height="598"
fill="none" stroke="blue" stroke-width="1" />
<path d="M100,300 Q-100,50 -300,300 Q-100,75 100,300"
fill="green" stroke="green" stroke-width="1" />
<!-- End points -->
<g fill="black" >
<circle cx="100" cy="300" r="3"/>
</g>
</svg>
(In general, the coordinates will not have the same Y position, this example is simplistic: but all I need is a way to determine the position of the endpoint in the proper coordinate system.)
This was hard for me to figure out, but a co-worker helped, and we finally puzzled through it. The solution turns out to be fairly straightforward.
The way we puzzled things out, it seems that there is a coordinate transformation matrix that can be used to convert from SVG user coordinates to browser pixels... and, via its inverse, back to SVG user coordinates. This is obtained for each starting and ending elements by:
start = document.getElementById('startSVGElement');
spt = start.createSVGPoint();
sCTM = start.getScreenCTM();
end = document.getElementById('endSVGElement');
ept = end.createSVGPoint();
eCTM = end.getScreenCTM();
Then, to draw from a point ( 100, 400 ) in the starting element, to a point ( 50, 60 ) in the ending element, you fill in the points:
spt.x = 100;
spt.y = 400;
ept.x = 50;
ept.y = 60;
Now the transformation magic: transform the ending point from SVG space to browser pixel space based on its CTM, and then transform back to SVG space using the starting element CTM inverse:
ept_in_start = ept.matrixTransform( eCTM ).matrixTransform( sCTM.inverse())
Now spt and ept_in_start have coordinates relative to the same element (the starting element) and in the same coordinate system (the SVG space of the starting element), and you can use those coordinates to draw anything you wish, based on the coordinates.
Of course, if the page reflows, you have to recalculate and redraw. And you have to be sure to get the right CTM for the (x,y) coordinates of each endpoint... there are lots of overlaid coordinate systems if you have nested SVGs and transformations.
I need a little bit of help.
In the following code, I want to control the rotation of the star via onmouse-event.
If you move the mouse over the star, it is supposed to rotate.
I thought about changing the transform in attributeName to something different when the mouse is not over the star via roationon()/off() functions so that the rotation doesn't work but I have no idea how to do that.
I appreciate every help I can get.
Thanks
<!DOCTYPE html>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<html>
<body>
<script>
function rotationon() {}
function rotationoff() {}
</script>
<svg height="2000" width="2000">
<polygon id="stern1" points="100,10 40,180 190,60 10,60 160,180" fill="yellow" transform="translate(100, 100)" onmouseover="rotationon()" onmouseout="rotationoff()" >
<animateTransform
attributeName="transform"
begin="0s"
dur="5s"
type="rotate"
from="0 100 100"
to="360 100 100"
repeatCount="indefinite"
/>
</polygon>
</svg>
</body>
</html>
There's a few different ways to approach this, depending on what its going to integrate with, and how well it will play with other browsers.
My gut instinct would be to say ultimately, its worth using one of the SVG libs out there, like Raphael, snap.svg, Pablo.js etc. They will help with some of the issues likely to be faced.
You can also just use pure SVG like I mentioned http://jsfiddle.net/xaM6q/
However, to use the method you are trying, you may want to use something like beginElement() and endElement, so the code could look something like the following...fiddle at http://jsfiddle.net/xaM6q/2/
<script>
function rotationon(evt){
document.getElementById('myanim').beginElement();
}
function rotationoff(){
document.getElementById('myanim').endElement();
}
</script>
<svg height="2000" width="2000">
<g transform="translate(100,100)">
<polygon id="stern1" points="100,10 40,180 190,60 10,60 160,180" fill="yellow" onmouseover="rotationon()" onmouseout="rotationoff()" >
<animateTransform
id="myanim"
attributeName="transform"
begin="indefinite"
dur="5s"
type="rotate"
from="0 100 100"
to="360 100 100"
fill="freeze"
repeatCount="indefinite"
/>
</polygon>
</g>
Couple of things worth noting. I've added a g element to help keep the transformation in place, as you probably want that (without it, you may find it moves away). Also the animation may be a bit erratic depending how you want it to stop (I've added 'fill=freeze'), and what happens with events mid animation.
Its worth knowing all of this to get to know SVG animations, but as mentioned, I would probably still look at using a 3rd party lib and control the rotation manually, rather than using the animate tag, so you can halt/restart a rotation at any angle easily.
I want to make a drawing effect of tree that looks something like this one with a progressive line like here. I would prefer using only css3 with svg/canvas and js. Do you have any ideas?
More info:
I tried to cut a tree into pieces and animate piece by piece the appearance but it's not cursive cause it's to much details on syncronizing delays and durations because every piece has a different length and so on. All of this is made without svg. I want to now if i can animate a line path.
Yes, take a look at this rendering of the Bahamas Logo using CSS 3
It describes his process, and techniques. Also you can view the source.
There are more that can be found here
Update:
Also maybe this Sencha Animator product may help?
You can do this with plain SVG. SVG provides the <animate> element for declarative animation.
What you want (as I understand it) is a line that appears as if it was drawn in front of the viewer's eyes. You can use the stroke-dasharray property for this purpose. This property defines a dash pattern using a series of values that defines the length of dashes and gaps. The strategy would be: First we have a dash that has length 0 and a gap that is at least as long as the whole path. This means we see nothing (or only the first point at the start of the path). In the end we want a dash that's at least the full length of the path. We want the dash to gradually become longer and longer until it reaches its final length (the length of the full path).
The simplest case would be:
<svg xmlns="http://www.w3.org/2000/svg" width="400px" height="300px">
<!-- This is the path of a spiral -->
<path d="m 7.1428565,220.9336 c 0,0 13.6660115,54.75386 218.5714335,51.42857 C 430.61971,269.03688 478.47682,99.194335 206.69537,110.78149 -65.086093,122.36866 45.497658,213.22607 210.28635,207.29759 375.07503,201.3691 429.75297,97.468925 207.14285,82.362175 -15.467268,67.255425 64.868608,160.66909 210,153.79075 c 145.13139,-6.87834 137.69998,-93.087405 11.42857,-99.999995 -126.271412,-6.9126 -150.382292,28.03248 -24.28571,35.71428 126.09659,7.6818 72.6601,-44.83727 -5.71429,-84.2857095"
stroke-width="10" stroke-linecap="round" fill="none" stroke="black" stroke-dasharray="0,2305">
<!-- This defines the animation:
The path is roughly 2305 units long, it will be drawn in 5 seconds -->
<animate from="0,2305" to="2305,0" dur="5s"
attributeName="stroke-dasharray" repeatCount="indefinite"/>
</path>
</svg>
More sophisticated things can be done using multiple values (using the values attribute) instead of one from and one to value:
<svg xmlns="http://www.w3.org/2000/svg" width="400px" height="300px">
<path d="m 7.1428565,220.9336 c 0,0 13.6660115,54.75386 218.5714335,51.42857 C 430.61971,269.03688 478.47682,99.194335 206.69537,110.78149 -65.086093,122.36866 45.497658,213.22607 210.28635,207.29759 375.07503,201.3691 429.75297,97.468925 207.14285,82.362175 -15.467268,67.255425 64.868608,160.66909 210,153.79075 c 145.13139,-6.87834 137.69998,-93.087405 11.42857,-99.999995 -126.271412,-6.9126 -150.382292,28.03248 -24.28571,35.71428 126.09659,7.6818 72.6601,-44.83727 -5.71429,-84.2857095"
stroke-width="10" stroke-linecap="round" fill="none" stroke="black" stroke-dasharray="0,2305">
<animate attributeName="stroke-dasharray" dur="5s" repeatCount="indefinite"
values="0,2305;
2000,305;
2305,0"/>
</path>
</svg>
You can specify the precise timing (when which value listed in values will be reached) using the keyTimes attribute:
<svg xmlns="http://www.w3.org/2000/svg" width="400px" height="300px">
<path d="m 7.1428565,220.9336 c 0,0 13.6660115,54.75386 218.5714335,51.42857 C 430.61971,269.03688 478.47682,99.194335 206.69537,110.78149 -65.086093,122.36866 45.497658,213.22607 210.28635,207.29759 375.07503,201.3691 429.75297,97.468925 207.14285,82.362175 -15.467268,67.255425 64.868608,160.66909 210,153.79075 c 145.13139,-6.87834 137.69998,-93.087405 11.42857,-99.999995 -126.271412,-6.9126 -150.382292,28.03248 -24.28571,35.71428 126.09659,7.6818 72.6601,-44.83727 -5.71429,-84.2857095"
stroke-width="10" stroke-linecap="round" fill="none" stroke="black" stroke-dasharray="0,2305">
<animate attributeName="stroke-dasharray" dur="5s" repeatCount="indefinite"
values="0,2305;
2000,305;
2305,0"
keyTimes="0;.9;1"/>
</path>
</svg>
See this in action on Tinkerbin.
Something similar can be done using CSS3:
<svg xmlns="http://www.w3.org/2000/svg" width="400px" height="300px">
<style type="text/css">
path {
animation-name:animateDash;
animation-duration:5s;
animation-iteration-count:infinite;
}
#keyframes animateDash {
from{stroke-dasharray:0,2305}
to {stroke-dasharray:2305,0}
}
</style>
<path d="m 7.1428565,220.9336 c 0,0 13.6660115,54.75386 218.5714335,51.42857 C 430.61971,269.03688 478.47682,99.194335 206.69537,110.78149 -65.086093,122.36866 45.497658,213.22607 210.28635,207.29759 375.07503,201.3691 429.75297,97.468925 207.14285,82.362175 -15.467268,67.255425 64.868608,160.66909 210,153.79075 c 145.13139,-6.87834 137.69998,-93.087405 11.42857,-99.999995 -126.271412,-6.9126 -150.382292,28.03248 -24.28571,35.71428 126.09659,7.6818 72.6601,-44.83727 -5.71429,-84.2857095"
stroke-width="10" stroke-linecap="round" fill="none" stroke="black" stroke-dasharray="0,2305"/>
</svg>
Decide for yourself which method you prefer.