SVG lines draw on scroll with jQuery - javascript

How i can create draw animation with css for SVG "line" element. I want to draw the line on scroll, with smooth effect. Some one have any ideas? I try to search this but i cant find this effect with line element.
here is my html and svg:
<div class="box">
<svg width="100%" height="100%">
<line x1="0" y1="0" x2="0" y2="100%" stroke="gray" stroke-width="2" />
<line x1="0" y1="100%" x2="100%" y2="100%" stroke="gray" stroke-width="2" />
</svg>
</div>
<div class="box2">
<svg width="100%" height="100%">
<line x1="100%" y1="100%" x2="100%" y2="0" stroke="gray" stroke-width="2" />
<line x1="0" y1="100%" x2="100%" y2="100%" stroke="gray" stroke-width="2" />
</svg>
</div>
<div class="box">
<svg width="100%" height="100%">
<line x1="0" y1="0" x2="0" y2="100%" stroke="gray" stroke-width="2" />
<line x1="0" y1="100%" x2="100%" y2="100%" stroke="gray" stroke-width="2" />
</svg>
</div>
<div class="box2">
<svg width="100%" height="100%">
<line x1="100%" y1="100%" x2="100%" y2="0" stroke="gray" stroke-width="2" />
<line x1="0" y1="100%" x2="100%" y2="100%" stroke="gray" stroke-width="2" />
</svg>
</div>
and css:
.box{
width: 100%;
height: 300px;
position: relative;
}
.box2{
width: 100%;
height: 300px;
position: relative;
}
Demo

I just know that you can run through a path with an custom javascript, maybe you can use that for a line aswell :)
NEW EDIT FOR SCROLLING FEATURE
// Get a reference to the <path>
var path = document.querySelector('#star-path');
// Get length of path... ~577px in this case
var pathLength = path.getTotalLength();
// Make very long dashes (the length of the path itself)
path.style.strokeDasharray = pathLength + ' ' + pathLength;
// Offset the dashes so the it appears hidden entirely
path.style.strokeDashoffset = pathLength;
// Jake Archibald says so
// https://jakearchibald.com/2013/animated-line-drawing-svg/
path.getBoundingClientRect();
// When the page scrolls...
window.addEventListener("scroll", function(e) {
// What % down is it?
// https://stackoverflow.com/questions/2387136/cross-browser-method-to-determine-vertical-scroll-percentage-in-javascript/2387222#2387222
// Had to try three or four differnet methods here. Kind of a cross-browser nightmare.
var scrollPercentage = (document.documentElement.scrollTop + document.body.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight);
// Length to offset the dashes
var drawLength = pathLength * scrollPercentage;
// Draw in reverse
path.style.strokeDashoffset = pathLength - drawLength;
// When complete, remove the dash array, otherwise shape isn't quite sharp
// Accounts for fuzzy math
if (scrollPercentage >= 0.99) {
path.style.strokeDasharray = "none";
} else {
path.style.strokeDasharray = pathLength + ' ' + pathLength;
}
});
body {
/* feel free to change height */
height: 5000px;
}
#star-svg {
position: fixed;
top: 50%;
left: 50%;
width: 150px;
height: 150px;
margin: -75px 0 0 -75px;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100.6 107.6" id="star-svg">
<path fill="none" stroke="black" stroke-width="2" id="star-path" d="M43.7,65.8L19.9,83.3c-2.9,1.9-5.1,3.2-7.9,3.2c-5.7,0-10.5-5.1-10.5-10.8
c0-4.8,3.8-8.2,7.3-9.8l27.9-12L8.8,41.4c-3.8-1.6-7.3-5.1-7.3-9.8c0-5.7,5.1-10.5,10.8-10.5c2.9,0,4.8,1,7.6,3.2l23.8,17.4
l-3.2-28.2c-1-6.7,3.5-12,9.8-12c6.3,0,10.8,5.1,9.8,11.7L57,41.8l23.8-17.4c2.9-2.2,5.1-3.2,7.9-3.2c5.7,0,10.5,4.8,10.5,10.5
c0,5.1-3.5,8.2-7.3,9.8L63.9,53.8l27.9,12c3.8,1.6,7.3,5.1,7.3,10.1c0,5.7-5.1,10.5-10.8,10.5c-2.5,0-4.8-1.3-7.6-3.2L57,65.8
l3.2,28.2c1,6.7-3.5,12-9.8,12c-6.3,0-10.8-5.1-9.8-11.7L43.7,65.8z"/>
</svg>
Source: https://css-tricks.com/scroll-drawing/

You need to export the svg in a path. and then you can add scroll animation depending on the viewport. Here is the testing server running your instance.demo project

Related

SVG getBoundingClientRect() + "transform: rotate()" is bugged in Chrome in a very specific way

getBoundingClientRect() works fine on all SVG elements except in the circumstance when an element is rotated in Chrome.
Below I have drawn 2 lines, the one on the left is diagonal and the right line starts vertical, as represented by the black lines. The green box is a visual representation of the getBoundingClientRect(). I then apply the same transform rotate to both lines at the same time and then update the bounding box visual.
As you can see from the following, the left bounding box does not represent the actual bounds, however, the right one does.
The problem is I need the true client bounds of the line in whatever orientation it is. This works fine in Firefox. Does anyone know of another way to get or calculate the bounds when a line, specifically, has a transform rotate?
I have posted a copy of this here: getBoundingClientRect() + "transform: rotate()" is bugged in Chrome?. To help people with a similar problem, but then decided I'd actually ask the community.
Chrome: v100.0.4896.75
This bug has now been reported to the cromium bugs board here: https://bugs.chromium.org/p/chromium/issues/detail?id=1314959
UPDATE: I've added the same 2 static lines with a direct transform attribute with a rotation of 40deg, and 2 div's representing the 'getBoundingClientRect'. The left box is clearly not showing the smallest rectangle which contains the entire element.
UPDATE 2 Included a BBox Visual in blue. getBBox() simply calculates the bounds of a child relative to the SVG element itself, prior to any rotation transforms. getBoundingClientRect does calculate the transform correctly, but is using the BBox, which has it's own issues here: https://bugs.chromium.org/p/chromium/issues/detail?id=377665
let l = [1,2,3,4,5,6].map(d => document.getElementById(`line${d}`)),
vis = [1,2,3,4,5,6].map(d => document.createElement(`div${d}`)),
bboxvis = [1,2,3,4,5,6].map(d => document.createElement(`div${d}`)),
r = 0;
vis.map((element) => {
document.body.appendChild(element)
element.style.position = "absolute"
element.style.border = "1px solid green"
element.style.background = `#0FF0002e`
})
bboxvis.map((element) => {
document.body.appendChild(element)
element.style.position = "absolute"
element.style.border = "1px solid blue"
element.style.background = `#0000FF2e`
})
let updateBounds = (element, displayElement, bboxvisEl) => {
let rect = element.getBoundingClientRect(),
svgBounds = element.parentElement.getBoundingClientRect(),
bbox = element.getBBox(),
d = document.documentElement;
displayElement.style.top = rect.y+d.scrollTop+"px"
displayElement.style.left = rect.x+d.scrollLeft+"px"
displayElement.style.width = rect.width+"px"
displayElement.style.height = rect.height+"px"
bboxvisEl.style.top = bbox.y+svgBounds.y+d.scrollTop + "px"
bboxvisEl.style.left = bbox.x+svgBounds.x+d.scrollLeft +"px"
bboxvisEl.style.width = bbox.width+"px"
bboxvisEl.style.height = bbox.height+"px"
}
[3,4,5].forEach(i => {
updateBounds(l[i], vis[i], bboxvis[i]);
bboxvis[i].style.transform = `rotate(40deg)`;
})
setInterval(() => {
[0,1,2].forEach(i => {
l[i].setAttribute("transform", `rotate(${r} 80 80)`);
bboxvis[i].style.transform = `rotate(${r}deg)`;
updateBounds(l[i], vis[i], bboxvis[i]);
})
r++;
if(r===360) r=0;
}, 20)
.container {
width:140px;
height:140px;
display:inline-block;
}
<svg xmlns="http://www.w3.org/2000/svg" class="container">
<line x1="40" y1="40" x2="120" y2="120" stroke="black" stroke-width="2"></line>
<line x1="40" y1="40" x2="120" y2="120" id="line1" stroke="red" stroke-width="2"></line>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" class="container">
<line x1="80" y1="30" x2="80" y2="130" stroke="black" stroke-width="2"></line>
<line x1="80" y1="30" x2="80" y2="130" id="line2" stroke="red" stroke-width="2"></line>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" class="container">
<line x1="60" y1="30" x2="100" y2="130" stroke="black" stroke-width="2"></line>
<line x1="60" y1="30" x2="100" y2="130" id="line3" stroke="red" stroke-width="2"></line>
</svg>
<br/>
<svg xmlns="http://www.w3.org/2000/svg" class="container">
<line x1="40" y1="40" x2="120" y2="120" id="line4" stroke="red" stroke-width="2" transform="rotate(40 80 80)"></line>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" class="container">
<line x1="80" y1="30" x2="80" y2="130" id="line5" stroke="red" stroke-width="2" transform="rotate(40 80 80)"></line>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" class="container">
<line x1="60" y1="30" x2="100" y2="130" id="line6" stroke="red" stroke-width="2" transform="rotate(40 80 80)"></line>
</svg>

CSS3 - Animate SVG if visible in viewport (Page Scroll)

I have been pulling my hair out trying to get this to work. Such a challenge.
The goal is to start the css animation when the object is visible, when it scrolls into view, and not before.
Using Javascript from here.
To make things easy, I have a code pen: https://codepen.io/studiotwofold/pen/dyYJWrV
Any ideas on how to fix this?
HTML:
<div class="icon-container" style="width:100px;">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 44 44" style="enable-background:new 0 0 44 44;" xml:space="preserve">
<defs>
<linearGradient id="icon-gradient" gradientUnits="userSpaceOnUse">
<stop offset="0%" style="stop-color:#E21C79"/>
<stop offset="100%" style="stop-color:#FC4C02"/>
</linearGradient>
</defs>
<g class="stf-icon">
<line x1="26" y1="29" x2="32" y2="29"/>
<path d="M29,25h-3l-0.2-0.5c-0.5-1.6-1.5-3.1-2.7-4.4l0,0c-3.3-3.3-3.2-8.8,0.3-12l0,0c3.2-2.9,8-2.9,11.2,0l0,0
c3.5,3.2,3.6,8.6,0.3,12l0,0c-1.2,1.2-2.2,2.7-2.7,4.4L32,25"/>
<line x1="29" y1="3" x2="29" y2="1"/>
<line x1="19.8" y1="7.3" x2="18.3" y2="6"/>
<line x1="23.9" y1="4.1" x2="23.1" y2="2.3"/>
<line x1="40.4" y1="11.3" x2="42.3" y2="10.7"/>
<line x1="38.1" y1="7" x2="39.6" y2="5.7"/>
<line x1="34" y1="3.8" x2="34.8" y2="2"/>
<path d="M17,13H3c-1.1,0-2,0.9-2,2v22c0,1.1,0.9,2,2,2h18l7,4v-4h7c1.1,0,2-0.9,2-2V24"/>
<line x1="5" y1="21" x2="11" y2="21"/>
<line x1="14" y1="21" x2="18" y2="21"/>
<line x1="5" y1="25" x2="13" y2="25"/>
<line x1="16" y1="25" x2="21" y2="25"/>
<line x1="5" y1="29" x2="8" y2="29"/>
<line x1="11" y1="29" x2="23" y2="29"/>
<line x1="5" y1="33" x2="13" y2="33"/>
<line x1="16" y1="33" x2="21" y2="33"/>
<polyline points="30.2,10 27,16 31,15 28.6,21 "/>
</g>
</svg>
</div>
CSS:
.stf-icon{fill:none;stroke: url(#icon-gradient);stroke-width:1;stroke-linecap:round;stroke-miterlimit:10;}
.animated-icon {
stroke-dasharray: 110;
stroke-dashoffset: 110;
animation: icon-animation 6s linear forwards;
animation-fill-mode: forwards;
}
#keyframes icon-animation {
from {stroke-dashoffset: 110;}
to {stroke-dashoffset: 0;}
}
Jquery:
var $fade = $(".stf-icon"); //Calling the class in HTML
$(window).scroll(function () { //Using the scroll global variable
$fade.each(function () {
fadeMiddle = $(this).offset().top + (0.4 *$(this).height());
windowBottom = $(window).scrollTop() + $(window).height();
if (fadeMiddle < windowBottom) {
$(this).addClass("animated-icon"); //add the animated icon class
}
});
});
/* On Load: Trigger Scroll Once*/
$(window).scroll();
To make things easy, I have a code pen: https://codepen.io/studiotwofold/pen/dyYJWrV
EDIT
Can this be done with straight javascript?
var wrapper = document.querySelector('.icon-container');
window.onscroll = function (event) {
if (wrapper.getBoundingClientRect().top < 0) {
wrapper.className = "icon-container animated-icon";
window.onscroll = null;
}
}

Can I make a transition on an SVG attribute that is not CSS?

I have an SVG that I would like to transition to extend when the mouse hovers over it, and ease back to its normal size when the mouse is not hovering. Because it is not adjusted through CSS, I can't figure out how to make that transition happen.
https://codepen.io/BrendanOB/pen/LYYepQQ
^ A link to an example of what I've got so far
Im new to javascript, any help is greatly appreciated, thank you.
I have already tried CSS transform scale and matrix, without working results.
<style>
.st2{fill:#E5CACA;}
.st3{fill:none;stroke:#FFF;stroke-width:17;stroke-linecap:round;stroke-miterlimit:10;}
</style>
<g id="Layer_2">
<line class="st3" x1="500" y1="142" x2="500" y2="95"/>
<line onmouseover="bigLine()" onmouseleave="smallLine()" id="move" class="st3" x1="518.2" y1="142.9" x2="521.8" y2="96.1"/>
<line id="stretch" class="st3" x1="536" y1="144.7" x2="544" y2="98.3"/>
</g>
<script>
function bigLine(){
var lines = document.querySelector('#Layer_2')
var l = lines.querySelector('#move')
console.log(l);
l.transition = "all 2s";
l.setAttribute("y2", "26");
}
function smallLine(){
var lines = document.querySelector('#Layer_2')
var l = lines.querySelector('#move')
console.log(l);
l.transition = "all 2s";
l.setAttribute("y2", "96");
}
</script>
As Robert Longson commented you can use SMIL animations: Inside the line #move there are 2 <animate> elements: the first for the mouseover and the second for mouseleave.
The first animation is changing the value of the x2 attribute of the line from="96.1" to="26". The second element has no from attribute but is animating the value of the y2 to="96.1" The duration of both animations is dur="1s" and fill="freeze" is similar to the animation-fill-mode: forwards from CSS.
I hope it helps.
.st2 {
fill: #e5caca;
}
.st3 {
fill: none;
stroke: #ddd;
stroke-width: 17;
stroke-linecap: round;
stroke-miterlimit: 10;
}
<svg viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;">
<g id="Layer_2">
<line class="st3" x1="500" y1="142" x2="500" y2="95"/>
<line id="move" class="st3" x1="518.2" y1="142.9" x2="521.8" y2="96.1">
<animate
attributeType="XML"
attributeName="y2"
from="96.1" to="26"
begin="mouseover"
dur="1s"
fill="freeze" />
<animate
attributeType="XML"
attributeName="y2"
to="96.1"
begin="mouseleave"
dur="1s"
fill="freeze" />
</line>
<line id="stretch" class="st3" x1="536" y1="144.7" x2="544" y2="98.3"/>
</g>
</svg>

Change an inline SVG's x and y with ecmascript

I am using an inline SVG in an SVG and have set some default x and y values. When I change them, the inline SVG moves accordingly. I am trying to change it with
var inlineSVG = document.getElementById("inlineSVG");
inlineSVG.style.x = "90";
and that adds style="x:90px;" but that doesn't actually affect the element.
It's weird (in my head) because this works with a rect but not with an svg.
Here is my actual code:
<?xml version='1.0' encoding='UTF-8'?>
<svg width='1000' height='360'
xmlns='http://www.w3.org/2000/svg'
xmlns:xlink="http://www.w3.org/1999/xlink"
onload='init(evt)'
>
<script type='text/ecmascript'>
function init(event){
var wing1 = document.getElementById("wing1");
wing1.style.x = "90";
}
</script>
<circle cx="200" cy="140" r="5" fill="red" />
<circle cx="220" cy="170" r="5" fill="red" />
<circle cx="180" cy="170" r="5" fill="red" />
<circle cx="220" cy="220" r="5" fill="red" />
<circle cx="180" cy="220" r="5" fill="red" />
<svg id="wing1" x="280" y="100" viewBox="0 0 350 300">
<g>
<g>
<g>
<ellipse fill="#E6E7E8" cx="229.505" cy="117.813" rx="5.862" ry="4.547"/>
</g>
<g>
<ellipse fill="#E6E7E8" cx="265.931" cy="117.819" rx="5.862" ry="4.547"/>
</g>
</g>
<g>
<g>
<ellipse fill="#E6E7E8" cx="229.191" cy="125.538" rx="5.862" ry="4.547"/>
</g>
<g>
<ellipse fill="#E6E7E8" cx="265.617" cy="125.531" rx="5.861" ry="4.547"/>
</g>
</g>
</g>
<ellipse fill="#E6E7E8" cx="247.244" cy="121.796" rx="20.635" ry="38.017"/>
</svg>
<rect id="square" x="0" y="470" width="50" height="50" fill="#BADA55" style="fill-opacity : 0.5" />
<line x1="0" y1="0" x2="1000" y2="360" style="stroke: yellowgreen;
stroke-width: 1;
stroke-dasharray: 10 1;"></line>
<line x1="0" y1="360" x2="1000" y2="0" style="stroke: yellowgreen;
stroke-width: 1;
stroke-dasharray: 10 1;"></line>
I tried adding !important to the value but it didn't work ( because I guess it doesn't count it as a valid number? ).
The solution is to directly change the x attribute like so:
selector.setAttribute("attr",val);

in svg: translate vs position x and y

When I want to position simple objects such as rect or line should I use transform attribute or x and y attributes?
// this
d3.selectAll('rect')
.attr('x', d => d)
.attr('y', 0)
// or this?
d3.selectAll('rect')
.attr("transform", d => `translate(${d}, 0)`);
What is the performance difference?
In SVG transform is not hardware accelerated. They have around the same performance for single elements (in my experience). However, I use transform more to move thing around because in SVG not all elements have a x or y attributes, consider...
<line x1="0" y1="0" x2="100" y2="100" />
<circle cx="100" cy="100" r="100" />
<path d="M 0 0 L 100 100" />
<rect x="0" y="0" width="100" height="100" />
You must write a different implementation for each of these elements if you are not using transform. One area where transform is indeed faster is moving a large number of elements, if you have...
<g transform="translate(100, 100)">
<line x1="0" y1="0" x2="100" y2="100" />
<circle cx="100" cy="100" r="100" />
<path d="M 0 0 L 100 100" />
<rect x="0" y="0" width="100" height="100" />
</g>
It will be less processing intensive than moving each element individually

Categories

Resources