SVG line drawing with marker on scroll page - javascript

trying to draw a growing arrow that will be linked on page scroll. I was able to attach the path to the scroll, but the marker doesn't move with the path.
Please, help how to make the marker move along with the end of the path? Perhaps the end of the arrow through the marker is not the best solution and you know how to do it correctly?
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h2>Scroll down this window to draw a triangle.</h2>
<p>Scroll back up to reverse the drawing.</p>
<svg id="mySVG" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<marker id="Triangle" viewBox="0 0 10 10" refX="0" refY="10"
markerUnits="strokeWidth" markerWidth="4" markerHeight="3"
orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="context-stroke"/>
</marker>
</defs>
<path fill="none" marker-end="url(#Triangle)" stroke="red" stroke-width="3" id="triangle" d="M150 0 L75 200 L225 200 Z" />
Sorry, your browser does not support inline SVG.
</svg>
</body>
</html>
body {
height: 2000px;
background: #f1f1f1;
}
#mySVG {
position: fixed;
top: 15%;
width: 400px;
height: 210px;
margin-left:-50px;
}
// Get the id of the <path> element and the length of <path>
var triangle = document.getElementById("triangle");
var length = triangle.getTotalLength();
// The start position of the drawing
triangle.style.strokeDasharray = length;
// Hide the triangle by offsetting dash. Remove this line to show the triangle before scroll draw
triangle.style.strokeDashoffset = length;
// Find scroll percentage on scroll (using cross-browser properties), and offset dash same amount as percentage scrolled
window.addEventListener("scroll", myFunction);
function myFunction() {
var scrollpercent = (document.body.scrollTop + document.documentElement.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight);
var draw = length * scrollpercent;
// Reverse the drawing (when scrolling upwards)
triangle.style.strokeDashoffset = length - draw;
}
snippet https://codepen.io/rostislav_blatman/pen/zYLPjQa
find this answer with css animation https://stackoverflow.com/a/75166942/21043600
but this is not the same. In my case need bind on scroll page

Related

SVG animation on scroll once

Hi everyone I can program, and I saw a few weeks ago, that SVG animations are pretty useful!
Now my question:
I want to animate text like this website: https://weaintplastic.com/ I do not want the other things, just the Animation! I tried a lot of things, Exported via Illustrator a svg but it did not worked out. I do not want to use, any tool from github!
My assumption is:
I have to animate any letter in a Word (like the website), such that I have to know the paths from the svg! But this makes problem! How can I get a animation like this? Lets assume I want the word for ABOUT ME for example, how can I achieve CSS and JS which does the job for any svg if I have the path, or do I have to do for every Letter an own CSS and JS?
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 315 45" style="enable-background:new 0 0 315 45;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;}
</style>
<g>
<path d="M78.55,26.83l-2,5.75h-2.57l6.53-18.3h2.99l6.56,18.3h-2.65l-2.05-5.75H78.55z M84.85,24.98l-1.88-5.27
c-0.43-1.19-0.71-2.28-1-3.34h-0.06c-0.29,1.09-0.6,2.2-0.97,3.31l-1.88,5.29H84.85z"/>
<path d="M92.61,32.58c0.06-0.9,0.11-2.23,0.11-3.39V13.31h2.48v8.25h0.06c0.88-1.47,2.48-2.42,4.71-2.42c3.42,0,5.85,2.71,5.82,6.7
c0,4.7-3.11,7.03-6.19,7.03c-2,0-3.59-0.73-4.62-2.47h-0.09l-0.11,2.17H92.61z M95.2,27.32c0,0.3,0.06,0.6,0.11,0.87
c0.48,1.66,1.94,2.8,3.76,2.8c2.62,0,4.19-2.04,4.19-5.05c0-2.63-1.43-4.89-4.11-4.89c-1.71,0-3.31,1.11-3.82,2.93
c-0.06,0.27-0.14,0.6-0.14,0.98V27.32z"/>
<path d="M121.44,25.9c0,4.86-3.54,6.98-6.87,6.98c-3.74,0-6.62-2.61-6.62-6.76c0-4.4,3.02-6.98,6.84-6.98
C118.75,19.15,121.44,21.89,121.44,25.9z M110.48,26.04c0,2.88,1.74,5.05,4.19,5.05c2.4,0,4.19-2.14,4.19-5.1
c0-2.23-1.17-5.05-4.14-5.05S110.48,23.54,110.48,26.04z"/>
<path d="M136.15,29c0,1.36,0.03,2.55,0.11,3.58h-2.22l-0.14-2.14h-0.06c-0.66,1.06-2.11,2.44-4.56,2.44
c-2.17,0-4.76-1.14-4.76-5.75v-7.68h2.51v7.27c0,2.5,0.8,4.18,3.08,4.18c1.68,0,2.85-1.11,3.31-2.17c0.14-0.35,0.23-0.79,0.23-1.22
v-8.06h2.51V29z"/>
<path d="M143.33,15.67v3.77h3.59v1.82h-3.59v7.09c0,1.63,0.48,2.55,1.88,2.55c0.66,0,1.14-0.08,1.45-0.16l0.11,1.79
c-0.48,0.19-1.25,0.33-2.22,0.33c-1.17,0-2.11-0.35-2.71-1c-0.71-0.71-0.97-1.87-0.97-3.42v-7.17h-2.14v-1.82h2.14v-3.15
L143.33,15.67z"/>
<path d="M172.08,24.55c-0.14-2.55-0.31-5.62-0.29-7.9h-0.09c-0.66,2.14-1.45,4.42-2.42,6.95l-3.39,8.88h-1.88l-3.11-8.71
c-0.91-2.58-1.68-4.94-2.22-7.11h-0.06c-0.06,2.28-0.2,5.35-0.37,8.09l-0.51,7.84h-2.37l1.34-18.3h3.17l3.28,8.85
c0.8,2.25,1.46,4.26,1.94,6.16h0.09c0.48-1.85,1.17-3.86,2.02-6.16l3.42-8.85h3.17l1.2,18.3h-2.42L172.08,24.55z"/>
<path d="M188.76,24h-7.47v6.6h8.33v1.98H178.8v-18.3h10.38v1.98h-7.9v5.78h7.47V24z"/>
</g>
<g>
<rect x="54.72" y="8.99" class="st0" width="69.62" height="7.96"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>
I tried figma and svgator, and so on, but this is not useful, since I want to DO the ANIMATION WHEN I SCROLL on the section. (Once like the website).
I am sorry if my question is not Perfect, I tried my best to be precise as possible, and searched first on the Forum before I asked.
The technique has to be a combination with SVGGeometryElement.getTotalLength, stroke-dasharray, stroke-dashoffset, and the Intersection Observer API.
The example you provided has a lot more complex SVG elements than your own example. If you inspect the SVG you'll see that each letter consists of 1 or more <polygon> elements that are being animated. Being that your own SVG is a bit simpler I've built the example below on it.
First thing that is important is that the animation in the example animates the stroke property of each polygon. They do that by calculating the entire length of each path and setting both the stroke-dasharray and stroke-dashoffset properties with that total calculated path length. This will push the stroke out of the svg to be invisible, setting up the animation. (Each path has a different length, so setting the properties manually is a tedious job, let JS do it).
Then use the Intersection Observer API to determine when certain elements come into view. In this case I've chosen to watch for whole sections to come into view. Whenever a section enters the viewport, it will add an animate class to the section. In CSS I've defined that the class will trigger an animation that will change the stroke-dashoffset property. This will cause the stroke to animate back into the SVG.
const paths = document.querySelectorAll('svg path');
const sections = document.querySelectorAll('section');
paths.forEach((path, index) => {
const length = path.getTotalLength();
path.style.strokeDashoffset = `${length}px`;
path.style.strokeDasharray = `${length}px`;
path.style.animationDelay = `${index * 250}ms`;
});
const onInterSection = (entries, observer) => {
for (const { isIntersecting, target } of entries) {
if (isIntersecting) {
target.classList.add('animate');
observer.unobserve(target);
}
}
};
const observer = new IntersectionObserver(onInterSection);
for (const section of sections) {
observer.observe(section);
}
.st0 {
fill: none;
}
section {
display: grid;
place-items: center;
height: 75vh;
}
section:first-of-type {
background-color: #f7f7f7;
}
section:nth-of-type(2) {
background-color: #d7d7d7;
}
section:nth-of-type(2) {
background-color: #e7e7e7;
}
path {
stroke: black;
stroke-width: 1px;
fill: none;
}
section.animate path {
animation: letter 2s 1s forwards ease-out;
}
#keyframes letter {
to {
stroke-dashoffset: 0;
}
}
<section>
<span>Scroll down to animation</span>
</section>
<section>
<span>A bit further</span>
</section>
<section>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 315 45" style="enable-background:new 0 0 315 45;" xml:space="preserve">
<path d="M78.55,26.83l-2,5.75h-2.57l6.53-18.3h2.99l6.56,18.3h-2.65l-2.05-5.75H78.55z M84.85,24.98l-1.88-5.27
c-0.43-1.19-0.71-2.28-1-3.34h-0.06c-0.29,1.09-0.6,2.2-0.97,3.31l-1.88,5.29H84.85z"/>
<path d="M92.61,32.58c0.06-0.9,0.11-2.23,0.11-3.39V13.31h2.48v8.25h0.06c0.88-1.47,2.48-2.42,4.71-2.42c3.42,0,5.85,2.71,5.82,6.7
c0,4.7-3.11,7.03-6.19,7.03c-2,0-3.59-0.73-4.62-2.47h-0.09l-0.11,2.17H92.61z M95.2,27.32c0,0.3,0.06,0.6,0.11,0.87
c0.48,1.66,1.94,2.8,3.76,2.8c2.62,0,4.19-2.04,4.19-5.05c0-2.63-1.43-4.89-4.11-4.89c-1.71,0-3.31,1.11-3.82,2.93
c-0.06,0.27-0.14,0.6-0.14,0.98V27.32z"/>
<path d="M121.44,25.9c0,4.86-3.54,6.98-6.87,6.98c-3.74,0-6.62-2.61-6.62-6.76c0-4.4,3.02-6.98,6.84-6.98
C118.75,19.15,121.44,21.89,121.44,25.9z M110.48,26.04c0,2.88,1.74,5.05,4.19,5.05c2.4,0,4.19-2.14,4.19-5.1
c0-2.23-1.17-5.05-4.14-5.05S110.48,23.54,110.48,26.04z"/>
<path d="M136.15,29c0,1.36,0.03,2.55,0.11,3.58h-2.22l-0.14-2.14h-0.06c-0.66,1.06-2.11,2.44-4.56,2.44
c-2.17,0-4.76-1.14-4.76-5.75v-7.68h2.51v7.27c0,2.5,0.8,4.18,3.08,4.18c1.68,0,2.85-1.11,3.31-2.17c0.14-0.35,0.23-0.79,0.23-1.22
v-8.06h2.51V29z"/>
<path d="M143.33,15.67v3.77h3.59v1.82h-3.59v7.09c0,1.63,0.48,2.55,1.88,2.55c0.66,0,1.14-0.08,1.45-0.16l0.11,1.79
c-0.48,0.19-1.25,0.33-2.22,0.33c-1.17,0-2.11-0.35-2.71-1c-0.71-0.71-0.97-1.87-0.97-3.42v-7.17h-2.14v-1.82h2.14v-3.15
L143.33,15.67z"/>
<path d="M172.08,24.55c-0.14-2.55-0.31-5.62-0.29-7.9h-0.09c-0.66,2.14-1.45,4.42-2.42,6.95l-3.39,8.88h-1.88l-3.11-8.71
c-0.91-2.58-1.68-4.94-2.22-7.11h-0.06c-0.06,2.28-0.2,5.35-0.37,8.09l-0.51,7.84h-2.37l1.34-18.3h3.17l3.28,8.85
c0.8,2.25,1.46,4.26,1.94,6.16h0.09c0.48-1.85,1.17-3.86,2.02-6.16l3.42-8.85h3.17l1.2,18.3h-2.42L172.08,24.55z"/>
<path d="M188.76,24h-7.47v6.6h8.33v1.98H178.8v-18.3h10.38v1.98h-7.9v5.78h7.47V24z"/>
<rect x="54.72" y="8.99" class="st0" width="69.62" height="7.96"/>
</svg>
</section>

Need to dynamically cut corners on rectangle using Javascript

I have a problem. I am creating the form where the user can enter coordinates x1,x2,y1,y2 for each corner to cut it top left, top right, bottom left, bottom right. Those coordinates are in millimeters and after the user enters data He should see the following image:
The racktangle itself I presented using div
<div id='detailPlot' style=' position: relative; width:300px;height:300px;background-color:white;border:1px solid black; ' ></div>
Can I cut those corners on that using Javascript and show result there?
I'd use SVG Path instead of the <div> element. You can draw lines between defined coordinates using it's d property:
<svg id="detailPlot" height="150px" width="150px">
<path d="M0 0 L100 0 L150 100 L150 150 L0 150 L0 0" stroke="black" stroke-width="1" fill="none" />
</svg>
Using javascript you'll be able to change it's d property at will:
var pathElement= document.querySelectorAll("path")[0];
pathElement.d = "M0 0 L300 0 L300 300 L0 300 L0 0"; // draw a rectangle

Large gaussian blur filter without revealing SVG border

I am trying to create a kind of "spotlight" effect over a large background image where the user can use the cursor to view parts of an image.
I'm currently applying a gaussian blur filter to a mask element which is positioned at the cursor and reveals parts of the large image.
I would like to have a higher blur amount in order to get a softer edge, but when I increase the value of the stdDeviation attribute inside the filter, the border of the SVG is revealing – I have attached two images to illustrate, also you can see it in this pen https://codepen.io/moevbiz/pen/YbwErx
Here is my code:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%">
<defs>
<filter id="filter">
<feGaussianBlur stdDeviation="50"/>
</filter>
<mask id="mask">
<ellipse id="ellipse" cx="50%" cy="50%" rx="100" ry="100" fill="white" filter="url(#filter)"></ellipse>
</mask>
</defs>
<image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/Harry-Potter-1-.jpg" width="100%" height="100%" mask="url(#mask)"></image>
</svg>
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/Harry-Potter-1-.jpg" alt="" />
body {
margin: 0;
padding: 0;
height: 100vh;
width: 100vw;
overflow:hidden;
}
img {
width: 100vw;
height: 100vh;
mask: url(#mask);
}
document.onmousemove=function(e) {
var mousecoords = getMousePos(e);
var mouseX = mousecoords.x;
var mouseY = mousecoords.y;
var ellipse = document.getElementById('ellipse');
ellipse.setAttribute('cx', mouseX);
ellipse.setAttribute('cy', mouseY)
};
function getMousePos(e) {
return {x:e.clientX,y:e.clientY};
}
current(i want to have a larger blur than this, not just increase the ellipse radius)
increased blur value with visible borders
Thankful for any hints!
You need to increase your Filter Region.
By default it is <filter x="-10%" y="-10%" width="120%" height="120%">. But if you want to use a large blur, you will need to increase it, to cater for the way that the blur spreads the pixels out further.
Something like <filter id="filter" x="-75%" y="-75%" width="250%" Height="250%"> works for your example.
https://codepen.io/PaulLeBeau/pen/qGboxR

How can I animate the tracing a SVG rectangle using JavaScript?

For a game I'm working on,
I would like to be able to draw a SVG rectangle; using a percentage value (50% would draw half the rectangle stroke).
I need to do this in Javascript since I'll update the value quite often.
<svg id="rectangle-timer" style="width:100%;height:100%;">
<rect width="100%" height="100%"/>
</svg>
I saw quite nice JS libraries like drawSVG or Vivus, but it seems that they work with paths, not with basic shapes like rectangles.
Can anyone help ?
Thanks.
The reason most libraries will use path elements is because of their inheritance from the SVGGeometryElement prototype, which gives you handy functions for computing the path length. So if we swap out this rectangle for a path like this:
<path d="M 0 0 L 1 0 L 1 1 L 0 1 z" />
We get exactly the same output, but its much more controllable. After that, we can just adjust the strokeDasharray value in the style to extend and remove some stroke. For this property we just need two values: initial dash size and initial empty space. So when our progress is 0, we want the first value to be 0 and the second to be the path length , and as we approach 1 we want the second value to 0 and the first one to increase to the path length.
function update( amount ){
const total = rect.getTotalLength();
const filled = total * amount;
const none = total - filled;
rect.style.strokeDasharray = `${filled} ${none}`;
}
const rect = document.getElementById( 'rectangle' );
const input = document.getElementById( 'input' );
input.addEventListener( 'mousemove', event => update( input.value ));
update( input.value );
<svg width="200px" height="200px" viewBox="0 0 200 200">
<path d="M 20 20 L 180 20 L 180 180 L 20 180 z" id="rectangle" fill="none" stroke="black" stroke-width="10" />
</svg>
<input id="input" type="range" min="0" max="1" step=".01" />
If you insist on using a rect, you could get a rectangle's path length by taking its width and height twice, which would look something like this:
function update( amount ){
const total = rect.width.baseVal.value * 2 + rect.height.baseVal.value * 2;
const filled = total * amount;
const none = total - filled;
rect.style.strokeDasharray = `${filled} ${none}`;
}
const rect = document.getElementById( 'rectangle' );
const input = document.getElementById( 'input' );
input.addEventListener( 'mousemove', event => update( input.value ));
update( input.value );
<svg width="200px" height="200px" viewBox="0 0 200 200">
<rect x="20" y="20" width="160" height="160" id="rectangle" fill="none" stroke="black" stroke-width="10" />
</svg>
<input id="input" type="range" min="0" max="1" step=".01" />
In the long run, however, this would mean less versatility, so I would suggest switching to path.
This is my solution: The SVG has preserveAspectRatio ="none" style="width:100%;height:100vh;" The total length of the path is 2*window.innerWidth + 2*window.innerHeight; Both stroke-dasharray and stroke-dashoffset are igual to the total length of the path.
I'm using an input type="range" to animate the stroke-dashoffset.
In order to preserve the stroke width and avoid stretching I'm using vector-effect="non-scaling-stroke"
I hope this is what you need.
function timer(){
let totalLength = 2*window.innerWidth + 2*window.innerHeight;
thePath.setAttributeNS(null, "style", `stroke-dashoffset:${totalLength * (1-range.value)}`)
}
timer()
range.addEventListener("input",timer);
setTimeout(function() {
timer()
addEventListener('resize', timer, false);
}, 15);
*{margin:0; padding:0;}
#thePath {
stroke-dasharray: calc(2 * 100vw + 2* 100vh);
stroke-dashoffset: calc(2 * 100vw + 2* 100vh);
}
#rectangle-timer{background:#dfdfdf}
[type="range"] {
position: absolute;
display: block;
width: 200px;
height: 20px;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
<svg id="rectangle-timer" viewBox="0 0 100 100" preserveAspectRatio ="none" style="width:100%;height:100vh;">
<path id="thePath" d="M0,0L100,0 100,100 0,100 0,0" fill="none" stroke="skyBlue" stroke-width="25" vector-effect="non-scaling-stroke" />
</svg>
<input type="range" id="range" value=".5" min="0" max="1" step=".01" />

Rotating teardrop shape in SVG around axis point

I'm trying to display a bunch of weather stations on a Leaflet map with SVG icons. Right now I'm just trying to get a handle on the drawing part.
The icons are tear shaped and should rotate around the "circle" part of the icon depending on the direction of the wind.
I'm having a hard time figuring out how to setup the transform and transform-origin so that it the icon "stays in place" and rotates around the "circle" in the path.
In the example below the number should stay in the middle of the circle.
var svg = d3.select('#icon svg');
// this is really done dynamically based on wind direction
var d = 0;
var path = svg.select('path');
// The animated rotation is just to make the example easy to verify.
function rotate(){
d = (d + 15 < 360) ? d + 15 : 0;
path.style('transform', 'rotate('+d+'deg)');
window.setTimeout(rotate, 60);
};
rotate();
svg {
overflow: visible;
background-color: #ffedfd
}
.station-icon path {
/** what am I supposed to use here? **/
transform-origin: center 40%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="icon" class="leaflet-marker-icon station-icon">
<svg width="26" height="26">
<g transform="translate(0,-6)">
<path class="st0" d="M26,19c0-2.2-0.6-4.4-1.6-6.2C22.2,8.8,13,0,13,0S3.8,8.7,1.6,12.8c-1,1.8-1.6,4-1.6,6.2c0,7.2,5.8,13,13,13
S26,26.2,26,19z"/>
</g>
<g>
<text x="13" y="13" font-family="sans-serif" font-size="20px" fill="white" text-anchor="middle" alignment-baseline="central">6</text>
</g>
</svg>
</div>
An alternative without using transform-origin is setting the center of the rotation (here, using magic numbers):
path.attr('transform', 'rotate('+d+' 13 19)');
var svg = d3.select('#icon svg');
// this is really done dynamically based on wind direction
var d = 0;
var path = svg.select('path');
// The animated rotation is just to make the example easy to verify.
function rotate(){
d = (d + 15 < 360) ? d + 15 : 0;
path.attr('transform', 'rotate('+d+' 13 19)');
window.setTimeout(rotate, 60);
};
rotate();
svg {
overflow: visible;
background-color: #ffedfd
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="icon" class="leaflet-marker-icon station-icon">
<svg width="26" height="26">
<g transform="translate(0,-6)">
<path class="st0" d="M26,19c0-2.2-0.6-4.4-1.6-6.2C22.2,8.8,13,0,13,0S3.8,8.7,1.6,12.8c-1,1.8-1.6,4-1.6,6.2c0,7.2,5.8,13,13,13
S26,26.2,26,19z"/>
</g>
<g>
<text x="13" y="13" font-family="sans-serif" font-size="20px" fill="white" text-anchor="middle" alignment-baseline="central">6</text>
</g>
</svg>
</div>
Does setting the origin to 60% achieve what you want?
var svg = d3.select('#icon svg');
// this is really done dynamically based on wind direction
var d = 0;
var path = svg.select('path');
function rotate(){
d = (d + 15 < 360) ? d + 15 : 0;
path.style('transform', 'rotate('+d+'deg)');
window.setTimeout(rotate, 60);
};
rotate();
svg {
overflow: visible;
background-color: #ffedfd
}
.station-icon path {
/** what am I supposed to use here? **/
transform-origin: center 60%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="icon" class="leaflet-marker-icon station-icon">
<svg width="26" height="26">
<g transform="translate(0,-6)">
<path class="st0" d="M26,19c0-2.2-0.6-4.4-1.6-6.2C22.2,8.8,13,0,13,0S3.8,8.7,1.6,12.8c-1,1.8-1.6,4-1.6,6.2c0,7.2,5.8,13,13,13
S26,26.2,26,19z"/>
</g>
<g>
<text x="13" y="13" font-family="sans-serif" font-size="20px" fill="white" text-anchor="middle" alignment-baseline="central">6</text>
</g>
</svg>
</div>

Categories

Resources