progressbar.js with image on the inside and text on the stroke - javascript

I want to reproduce the progress bar in the image below
but I am having trouble with the center image and the text on the side. I am using progressbar.js and this is where I am at right now:
// progressbar.js#1.0.0 version is used
// Docs: http://progressbarjs.readthedocs.org/en/1.0.0/
var bar = new ProgressBar.Circle(container, {
strokeWidth: 20,
easing: 'easeInOut',
duration: 1400,
color: 'url(#gradient)',
trailColor: '#eee',
trailWidth: 1,
svgStyle: null
});
let linearGradient = `
<defs>
<linearGradient id="gradient" x1="100%" y1="0%" x2="0%" y2="0%" gradientUnits="userSpaceOnUse">
<stop offset="20%" stop-color="mediumaquamarine"/>
<stop offset="50%" stop-color="turquoise"/>
</linearGradient>
</defs>
`
let percentage_text = "<span>100%</span>";
bar.svg.insertAdjacentHTML('afterBegin', linearGradient);
bar.svg.insertAdjacentHTML('beforeend', percentage_text);
bar.animate(1.0); // Number from 0.0 to 1.0
#container {
margin: 20px;
width: 200px;
height: 200px;
}
span{
position: absolute;
z-index: 9;
width: 90px;
height: 90px;
color: black;
}
<script src="https://rawgit.com/kimmobrunfeldt/progressbar.js/1.0.0/dist/progressbar.js"></script>
<div id="container"></div>
I have tried inserting the percentage as a span but it doesn't show, for the image I think I can add another div on top of this one with the image in the center but it wouldn't be perfectly centered and might overlap the progress bar.
Any help would go a long way, and if there is another better library to imitate the image it would be perfect. thanks in advance.

I hope this helps you. Clean the code as you want!
Add an id for second path in svg for percentage:
let path = document.querySelector('svg path:last-child').setAttribute("id", "MyPath");
Add the step key to your bar:
step: (state, bar) => {
var value = Math.round(bar.value() * 100);
update(value)
}
Add an update method to handle the percentage on the path:
function update(val) {
if (!bar) return;
if (document.getElementById('text-tp'))
document.getElementById('text-tp').outerHTML = "";
bar.svg.insertAdjacentHTML('beforeend',
`<text id="text-tp"> <textPath id="tp" href="#MyPath">${val}%</textPath> </text>`);
let tp = document.getElementById('tp');
if (val > 7)
tp.setAttributeNS(null, "startOffset", val - 7 + "%");
else tp.setAttributeNS(null, "startOffset", val + "%");
}
Add clipPath to your defs part
<clipPath id="circleView">
<circle cx="50" cy="50" r="30" fill="none" />
</clipPath>
Add image to your svg:
bar.svg.insertAdjacentHTML('beforeend',
` <image
width="250"
height="150" xlink:href="https://www.amrita.edu/sites/default/files/news-images/new/news-events/images/l-nov/grass.jpg"
clip-path="url(#circleView)"
/>`);
Full code is:
var bar = new ProgressBar.Circle(container, {
strokeWidth: 20,
easing: 'easeInOut',
duration: 4400,
color: 'url(#gradient)',
trailColor: '#eee',
trailWidth: 1,
svgStyle: null,
step: (state, bar) => {
var value = Math.round(bar.value() * 100);
update(value)
}
});
let linearGradient = `
<defs>
<linearGradient id="gradient" x1="100%" y1="0%" x2="0%" y2="0%" gradientUnits="userSpaceOnUse">
<stop offset="20%" stop-color="mediumaquamarine"/>
<stop offset="50%" stop-color="turquoise"/>
</linearGradient>
<clipPath id="circleView">
<circle cx="50" cy="50" r="30" fill="none" />
</clipPath>
</defs>
`
bar.svg.insertAdjacentHTML('afterBegin', linearGradient);
let path = document.querySelector('svg path:last-child').setAttribute("id", "MyPath");
bar.svg.insertAdjacentHTML('beforeend',
` <image
width="250"
height="150" xlink:href="https://www.amrita.edu/sites/default/files/news-images/new/news-events/images/l-nov/grass.jpg"
clip-path="url(#circleView)"
/>`);
function update(val) {
if (!bar) return;
if (document.getElementById('text-tp'))
document.getElementById('text-tp').outerHTML = "";
bar.svg.insertAdjacentHTML('beforeend',
`<text id="text-tp"> <textPath id="tp" href="#MyPath">${val}%</textPath> </text>`);
let tp = document.getElementById('tp');
if (val > 7)
tp.setAttributeNS(null, "startOffset", val - 7 + "%");
else tp.setAttributeNS(null, "startOffset", val + "%");
}
bar.animate(1.0); // Number from 0.0 to 1.0
#container {
margin: 20px;
width: 200px;
height: 200px;
}
span {
position: absolute;
z-index: 9;
width: 90px;
height: 90px;
color: black;
}
#text-tp {
position: absolute;
font-size: 8px;
}
<script src="https://rawgit.com/kimmobrunfeldt/progressbar.js/1.0.0/dist/progressbar.js"></script>
<div id="container"></div>

Related

Re-Center text position as str length changes

I am new to javascript and am having an issue repositioning animated text as the string length varies. I have an SVG element and a string within it, where that string needs to be centered within that SVG. Using ' | ' as a center reference, the centering would look like:
| | |
g g g g g g
If I start the animation with a str of len 3, it will be centered properly for Len 3 strs, but then other lens would be equivalent to:
| |
g g g
Example code:
function animateValue(obj, start, end, duration) {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
const str = obj.innerHTML;
// console.log(`${str.length}` );
if (`${str.length}`==="1"){
obj.style.x = '200px';
}
obj.innerHTML = Math.floor(progress * (end - start) + start);
if (progress < 1) {
window.requestAnimationFrame(step);
}
};
window.requestAnimationFrame(step);
}
const obj = document.getElementById("heading");
animateValue(obj, 100, 0, 5000);
svg {
position: absolute ;
width: 40%;
border: 1px solid rgba(255,255,255,0.3);
margin-left: 30%;
border-radius: 50%;
}
#roseline, #majline {
stroke: #eee;
stroke-width: .5;
}
text {
font-family: Montserrat, sans-serif;
font-size: 10;
fill: #eee;
}
text.heading1{
font-size:4.5em;
fill: #0ee;
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 500 500" id="compassrose">
<defs>
<symbol>
<line x1="40" y1="250" x2="50" y2="250" id="roseline" />
<line x1="40" y1="250" x2="60" y2="250" id="majline" />
<path d="M10,250a240,240 0 1,0 480,0a240,240 0 1,0 -480,0" id="rosecircle" transform='rotate(90 250 250)' />
</symbol>
</defs>
<div class="triangle-container">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 500 500" id="compassrose">
<polygon points="250,40 280,0 220,000" class="triangle" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 500 500" >
<polygon points="0,260 0,220 40,240" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 500 500" >
<polygon points="500,260 500,220 460,240" />
<text class="heading1" id="heading" x='190px' y='250px'
fontSize="36">100 </text>
</svg>
</div>
</svg>
I have tried re-arranging the divs to allow for the absolute and relative positioning, however that was not properly maintaining size relationships as needed.
If you use dominant-baseline="middle" text-anchor="middle" on the text element and position it in the middle of the SVG (250,250) it should work.
function animateValue(obj, start, end, duration) {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
obj.innerHTML = Math.floor(progress * (end - start) + start);
if (progress < 1) {
window.requestAnimationFrame(step);
}
};
window.requestAnimationFrame(step);
}
const obj = document.getElementById("heading");
animateValue(obj, 200, 0, 5000);
svg {
display: block;
position: absolute;
width: 40%;
border: 1px solid rgba(255, 255, 255, .3);
margin-left: 30%;
border-radius: 50%;
}
#roseline,
#majline {
stroke: #eee;
stroke-width: .5;
}
text {
font-family: Montserrat, sans-serif;
font-size: 10;
fill: #eee;
}
text.heading1 {
fill: #0ee;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" id="compassrose">
<defs>
<symbol>
<line x1="40" y1="250" x2="50" y2="250" id="roseline" />
<line x1="40" y1="250" x2="60" y2="250" id="majline" />
<path d="M10,250a240,240 0 1,0 480,0a240,240 0 1,0 -480,0"
id="rosecircle" transform='rotate(90 250 250)' />
</symbol>
</defs>
<polygon points="250,40 280,0 220,000" class="triangle" />
<polygon points="0,260 0,220 40,240" />
<polygon points="500,260 500,220 460,240" />
<text class="heading1" id="heading" x="250" y="250" font-size="60"
dominant-baseline="middle" text-anchor="middle">100</text>
</svg>

SVG Linear Gradient SVG Background

I am trying to use an SVG, which will be dynamically created from JavaScript, as the background image on another SVG. This works when the fill color of the object is a solid color, but not when I try to use a linear gradient. Run the code to see an example. Please help figure how to use the linear gradient!
const createElement = (tag, attributes) => {
const element = document.createElement(tag);
if (attributes) Object.keys(attributes).forEach(key => element.setAttribute(key, attributes[key]));
return element;
}
// Create background for first SVG using solid color fill:
const svg3bg = createElement('svg', { xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 50 50', width: 50, height: 50 });
svg3bg.appendChild(createElement('circle', { cx: 25, cy: 25, r: 20, fill: '#00F' }));
const svg3 = document.getElementById('svg3');
svg3.style.backgroundImage = `url('data:image/svg+xml,${svg3bg.outerHTML.replace(/\#/g, '%23')}')`; // This does not display unless I replace the # signs with a hex code.
svg3.style.backgroundColor = 'palegreen';
// Create background for second SVG using linear gradient fill:
const svg4bg = createElement('svg', { xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 50 50', width: 50, height: 50 });
const lg4 = svg4bg.appendChild(createElement('linearGradient', { id: "lg4" }))
lg4.appendChild(createElement('stop', { offset: "0%", 'stop-color': '#d67ef5' }))
lg4.appendChild(createElement('stop', { offset: "50%", 'stop-color': '#2b78ba' }))
lg4.appendChild(createElement('stop', { offset: "100%", 'stop-color': '#4d79a9' }))
svg4bg.appendChild(createElement('circle', { cx: 25, cy: 25, r: 20, fill: 'url(#lg4)' }));
const svg4 = document.getElementById('svg4');
svg4.style.backgroundImage = `url('data:image/svg+xml,${svg4bg.outerHTML.replace(/\#/g, '%23')}')`;
svg4.style.backgroundColor = 'palegreen';
This shows an SVG using another SVG (generated from JavaScript) of blue dots as its background image:
<div id="div3">
<svg id="svg3" iewBox="0 0 100 100" style="width: 150; height: 150;">
<rect x="10" y="10" width="80" height="80" style="stroke: black; fill: none; stroke-width: 4"></rect>
</svg>
</div><br>
When trying to do the same thing with a linear gradient to fill the object instead of a solid color, it does not
display:
<div id="div4">
<svg id="svg4" iewBox="0 0 100 100" style="width: 150; height: 150;">
<rect x="10" y="10" width="80" height="80" style="stroke: black; fill: none; stroke-width: 4"></rect>
</svg>
</div><br>
This shows what the background image with the linear gradient should look like:
<div id="div5">
<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 50 50" width="50" height="50">
<lineargradient id="lg5">
<stop offset="0%" stop-color="#d67ef5"></stop>
<stop offset="50%" stop-color="#2b78ba"></stop>
<stop offset="100%" stop-color="#4d79a9"></stop>
</lineargradient>
<circle cx="25" cy="25" r="20" fill="url(#lg5)"></circle>
</svg>
</div>
Seems OK to me once I correct all the typos.
viewBox is missing a v in some places
units are missing from CSS sizes where they are mandatory
use createElementNS to create SVG elements and use an XML serializer rather than HTML serialization to get namespaces in the output.
while not necessary in this case I've fixed the URI encoding properly rather than using replace
const createElement = (tag, attributes) => {
const element = document.createElementNS('http://www.w3.org/2000/svg', tag);
if (attributes) Object.keys(attributes).forEach(key => element.setAttribute(key, attributes[key]));
return element;
}
let s = new XMLSerializer();
// Create background for first SVG using solid color fill:
const svg3bg = createElement('svg', { viewBox: '0 0 50 50', width: 50, height: 50 });
svg3bg.appendChild(createElement('circle', { cx: 25, cy: 25, r: 20, fill: '#00F' }));
const svg3 = document.getElementById('svg3');
svg3.style.backgroundImage = `url('data:image/svg+xml,${encodeURIComponent(s.serializeToString(svg3bg))}')`;
svg3.style.backgroundColor = 'palegreen';
// Create background for second SVG using linear gradient fill:
const svg4bg = createElement('svg', { viewBox: '0 0 50 50', width: 50, height: 50 });
const lg4 = svg4bg.appendChild(createElement('linearGradient', { id: "lg4" }))
lg4.appendChild(createElement('stop', { offset: "0%", 'stop-color': '#d67ef5' }))
lg4.appendChild(createElement('stop', { offset: "50%", 'stop-color': '#2b78ba' }))
lg4.appendChild(createElement('stop', { offset: "100%", 'stop-color': '#4d79a9' }))
svg4bg.appendChild(createElement('circle', { cx: 25, cy: 25, r: 20, fill: 'url(#lg4)' }));
const svg4 = document.getElementById('svg4');
svg4.style.backgroundImage = `url('data:image/svg+xml,${encodeURIComponent(s.serializeToString(svg4bg))}')`;
svg4.style.backgroundColor = 'palegreen';
This shows an SVG using another SVG (generated from JavaScript) of blue dots as its background image:
<div id="div3">
<svg id="svg3" viewBox="0 0 100 100" style="width: 150px; height: 150px;">
<rect x="10" y="10" width="80" height="80" style="stroke: black; fill: none; stroke-width: 4"></rect>
</svg>
</div><br>
When trying to do the same thing with a linear gradient to fill the object instead of a solid color, it does not
display:
<div id="div4">
<svg id="svg4" viewBox="0 0 100 100" style="width: 150px; height: 150px;">
<rect x="10" y="10" width="80" height="80" style="stroke: black; fill: none; stroke-width: 4"></rect>
</svg>
</div><br>
This shows what the background image with the linear gradient should look like:
<div id="div5">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50" height="50">
<linearGradient id="lg5">
<stop offset="0%" stop-color="#d67ef5"></stop>
<stop offset="50%" stop-color="#2b78ba"></stop>
<stop offset="100%" stop-color="#4d79a9"></stop>
</linearGradient>
<circle cx="25" cy="25" r="20" fill="url(#lg5)"></circle>
</svg>
</div>

Create a gauge chart in svg or css

I need to implement a chart that looks like this.
https://i.stack.imgur.com/jSIUp.png
I tried using in svg but I'm not able to achieve what I've needed. The code I've used is the following:
svg {
height: 90vh;
margin: auto;
display: block;
}
path.purple {
stroke: url(#gradient);
stroke-dasharray: 282;
stroke-dashoffset: 282;
animation: dash 5s linear forwards;
}
#keyframes dash {
to {
stroke-dashoffset: 0;
}
}
<svg style="fill:none; stroke:#81125A" width="400" height="400">
<linearGradient id="gradient" x1="0" y1="0" x2="0" y2="100%">
<stop offset="0%" stop-color="#81125A" />
<stop offset="100%" stop-color="#81125A" />
</linearGradient>
<path d=" M 124.58399310102934 183.5 A 79 75 0 0 1 118.30403252765396 121.58238841571327"stroke="#e7e7e8" stroke-dasharray="65,65"></path>
<path d=" M 123.90504313598774 109.6392784815247 A 79 75 0 0 1 198.51076142578597 71.18269623051319" stroke="#e7e7e8" stroke-dasharray="90, 90"></path>
<path d=" M 212.11182975237372 73.22782052930026 A 79 75 0 0 1 267.2357170420868 120.34848925057486" stroke="#e7e7e8" stroke-dasharray="75, 75"></path>
<path d=" M 270.79981248796446 132.97638667498023 A 79 75 0 0 1 259.99579959635764 185.74394481749036" stroke="#e7e7e8" stroke-dasharray="60,60"></path>
</svg>
Any help is greatly appreciated!
Does it have to look exactly like that? You can get a pretty quick version with ChartJS.
const ctx = document.getElementById("chart");
new Chart(ctx, {
type: "doughnut",
data: {
datasets: [{
data: [700, 200],
backgroundColor: ["rgb(133, 13, 123)", "rgb(235, 235, 235)"]
}, ],
},
options: {
rotation: Math.PI + 50,
circumference: Math.PI + 0.5,
cutoutPercentage: 90
},
});
#container {
position: relative;
width: 300px;
height: 300px;
margin-top: -50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<div id="container">
<canvas id="chart" width="100%" height="100%"></canvas>
</div>

Isolate section of SVG when mouse is hovered

I am attempting to build an interactive map from scratch. I have an SVG of the map of Canada that has separate layers for each province. I'd like to highlight a specific province/territory when the mouse is hovered over it.
I have tried using a function that gets the live update of the mouse coordinates, but I am unsure as to how to figure out which section of the image it is on. I am unsure as to if this is the best method of doing it.
Through some of my research, I have found a method of gathering the pointer coordinates:
let x = 0;
let y = 0;
function showCoords() {
x = event.clientX;
y = event.clientY;
}
And I call it around the SVG using:
<div onmousemove="showCoords()">
...SVG HERE
</div>
I can select the different layers inside of the SVG, but the question is if I can find a way of checking which layer my mouse is currently over.
Thank you!
Edit:
I have reached a solution that accomplishes my goal, but I am unsure as to if it is efficient.
Using CSS, I fill each group upon hover:
#canada {
fill: hsl(0, 0%, 29%);
}
#canada g {
transition: .3s;
}
#canada g:hover {
fill: hsl(189, 88%, 59%);
}
I am also using the onClick option on each group to pull up their dialogue.
Maybe something like this? Just keep "data-name" attribute on each <g> inside svg code.
let x = 0;
let y = 0;
let svgCountry = document.querySelector("#states svg");
let svgStates = svgCountry.querySelectorAll("[data-name]");
let resContainer = document.getElementById("state-hovered-is");
// init tooltip
function showCoords(event) {
let x = event.clientX;
let y = event.clientY;
resContainer.setAttribute("style", "left:" + (x + 15) + "px;" + "top:" + (y - 50) + "px;");
}
// close tooltip
function clearCoor() {
resContainer.innerHTML = "";
}
// clear everything
function clearAllOn() {
svgStates.forEach(function(el) {
el.classList.remove("hover");
resContainer.innerHTML = "";
console.clear();
});
}
// display current state in div
function displayState(el) {
var res = el.getAttribute("data-name");
el.classList.add("hover");
resContainer.innerHTML = "<p>You're at" + " " + res + "</p>";
console.log(res);
}
svgStates.forEach(function(el) {
// mouse events
el.addEventListener("mouseenter", function() {
displayState(el);
});
el.addEventListener("mouseleave", function() {
clearAllOn();
});
// touch events
el.addEventListener("touchstart", function() {
clearAllOn();
displayState(el);
});
});
body {
background-color: #fff;
font-family: Arial, Helvetica, sans-serif;
color: #333;
}
g.hover {
opacity: 0.5;
}
#states {
margin: 0 auto;
width: 600px;
max-width: 100%;
position: relative;
}
#state-hovered-is {
text-align: center;
min-height: 50px;
position: fixed;
pointer-events: none;
background-color: rgba(0, 0, 0, 0.76);
border-radius: 6px;
}
#state-hovered-is p {
color: #fff;
margin: 0;
padding: 15px;
width: 180px;
}
<div id="states" onmousemove="showCoords(event)" onmouseout="clearCoor()">
<!-- State name here -->
<div id="state-hovered-is"></div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 961.38 830.7">
<defs>
<style>
.cls-1 { fill: #fff;}
.cls-2 {fill: #b8c4e6;}
.cls-3 {fill: #ce8080;}
.cls-4 {fill: #e99bd2;}
.cls-5 {fill: #acac90;}
.cls-6 {fill: #e6c0c0;}
.cls-7 {fill: #4d6ac1;}
.cls-8 {fill: #c9e6b8;}
.cls-9 {fill: #f3f3db;}
.cls-10 {fill: #cece71;}
.cls-11 {fill: #7188ce;}
.cls-12 {fill: #daa0a0;}
.cls-13 {fill: #91916b;}
.cls-14 {fill: #dada94;}
.cls-15 {fill: #916b86;}
</style>
</defs>
<title>Canada</title>
<g id="Layer_1">
<ellipse class="cls-1" cx="480.69" cy="415.35" rx="679.8" ry="587.39" />
<path class="cls-1" d="M539.84,220.75c-2.45,5-6.57,8.18-10.72,11.7l2-9.56C534.53,224.17,537,221.84,539.84,220.75Z" transform="translate(-0.06 -0.26)" />
<path class="cls-1" d="M53,543.56c-1.09,2.13-2.18,4.23-3.52,6.84l-.51-10.68.56-.25Z" transform="translate(-0.06 -0.26)" />
<path class="cls-1" d="M771.19,558.14c-.32-2.18-.77-4.36-.89-6.56a3.15,3.15,0,0,1,1.31-2.05,1.76,1.76,0,0,1,1.64.49,4.19,4.19,0,0,1,.17,2.5c-.48,1.84-1.18,3.62-1.79,5.43Z" transform="translate(-0.06 -0.26)" />
<path class="cls-1" d="M40.77,527.77l-7-8.41.35-.18A76,76,0,0,1,40.4,524c.77.71.87,2.14,1.27,3.25Z" transform="translate(-0.06 -0.26)" />
<path class="cls-1" d="M493.56,95.47l3.41,6L485.7,105a30.91,30.91,0,0,1,2.81-2.42C491.7,100.51,493.64,100.2,493.56,95.47Z" transform="translate(-0.06 -0.26)" />
</g>
<g id="Yukon" data-name="Yukon">
<path class="cls-3" d="M174.12,185.61c.91,1.61.88,2.8-.63,4.31-4.21,4.21-8.16,8.68-12.2,13.07-.34.38-.78,1-.72,1.39.58,3.9-2,6.08-4.31,8.53a6.48,6.48,0,0,0-1.84,3.83,5.33,5.33,0,0,0,1.79,3.5c1.85,1.68,4.07,2.94,6,4.53,3.45,2.83,3.47,5.7.33,9.15-2.19,2.39-4.41,4.77-6.37,7.34-1.76,2.3-1.12,4.05,1.42,5.49,1.4.81,2.66,1.88,4.19,3-2.18,1.51-3.35,2.81-2.33,5.22.45,1.05,0,2.31-1.73,2.4a17.09,17.09,0,0,0-3.62.83c-4.36,1.21-5.4,4.72-5.53,8.56a57.14,57.14,0,0,0,.5,10c.23,1.57,1.5,3,2.39,4.63-2.26,2.84-2.38,6.32-1.6,10.12a46.41,46.41,0,0,1,.52,7.56,1.6,1.6,0,0,1-.82,1.17c-2.55,1.24-3,3.61-3.48,6a9.38,9.38,0,0,1-1.27,3.48,10.68,10.68,0,0,0-1.7,8.87,2.39,2.39,0,0,1-.44,1.48c-.6,1-1.32,2-2,2.93-1.42,2.18-1.43,4,.77,5.24,3.32,1.83,3.1,4.7,2.57,7.41-.72,3.66-.45,6.87,2,9.78,1.47,1.77,1.39,3.3-.37,4.95a7.72,7.72,0,0,0-1.45,3.08c-.28.81-.27,1.71-.5,2.53-1.52,5.43,1,6.26,4.74,7.61,1.41.5,2.57,1.66,3.93,2.34a26.86,26.86,0,0,0,4.63,1.87c1.72.5,3.51.73,5.24,1.06.05.25.15.5.1.53-3,2.14-3,5.28-2.81,8.48.09,2,0,4,0,6.25A575.11,575.11,0,0,1,48.14,298a7.87,7.87,0,0,1,.58-.75c3.2-3.33,3.13-5.09-.71-7.55a38.29,38.29,0,0,0-6.52-3.12c-2.14-.86-3.05-5-1.32-6.73q13.23-13,26.49-26c6.81-6.67,13.65-13.31,20.46-20q13.26-13,26.5-26,10.56-10.35,21.15-20.68,11.07-10.83,22.16-21.66c1.92-1.87,3.87-3.74,6-5.43a2.44,2.44,0,0,1,2.34-.08,12.63,12.63,0,0,1,4.51,7.68c.95,5.52,2.44,10.95,3.73,16.42A5.64,5.64,0,0,0,174.12,185.61Z" transform="translate(-0.06 -0.26)" />
</g>
<g id="British_Columiba" data-name="British Columiba">
<g>
<path class="cls-4" d="M190.26,405.28c.27.14.53.31,1.06.62-1,1.88-2,3.69-2.9,5.53-5.64,11.14-11.24,22.3-16.9,33.43-4.43,8.73-8.92,17.42-13.38,26.13l-19.37,37.93c-.21.42-.68.84-.65,1.24.17,2.7,0,5.24-2.52,6.95.78,2,1.25,4.14,2.45,5.8.82,1.15,1.83,1.89,1.89,3.54s.46,3.46,2.33,4.55c2.39,1.41,1.92,4.19,2.3,6.41.31,1.81-.17,3.77-.35,5.66s.8,3.35,2.22,4.62a2.72,2.72,0,0,1,.81,2.17,5.54,5.54,0,0,0,1.49,5.42c.88,1,1.18,2.44,1.69,3.71.82,2,1.6,4,2.38,6,1,2.51,2,5,2.9,7.55.32.88.14,2.18.72,2.67,3.23,2.74,2.75,6.63,3,10.1a15.41,15.41,0,0,0,3.74,8.9c3.16,3.57,2.61,7,1.68,10.73-.83,3.31-1.92,6.57-3,9.84s-.35,6.2,1.21,9.74c-5.31-2.27-10.13-4.28-14.9-6.38-10.53-4.64-21.08-9.23-31.54-14-8.14-3.72-16.25-7.52-24.19-11.65-8.81-4.6-17.41-9.61-26.09-14.47a2,2,0,0,1-1-1.12,65.21,65.21,0,0,1-1-7.47c-.16-3.6-1.05-5-4.37-5.94,0-1.19,0-2.34,0-3.5a3.82,3.82,0,0,0-2.71-3.76c-1.17-.5-3.27-.93-3.32-1.53a9.88,9.88,0,0,1,.91-5.44c3.08-5.49,3.08-7.06-1-11.44-2.29-2.47-4.78-4.79-6.82-7.46a9.16,9.16,0,0,1-1.44-4.76c-.2-2.61-1.08-4.69-3.49-5.72-3.27-1.39-4.67-4.2-5.48-7.23a5.27,5.27,0,0,1,1-4.36,5.48,5.48,0,0,0,1.59-5.2c-.48-3.26,1.93-4.89,4.78-5.55a19.58,19.58,0,0,0,3.83-1c2.43-1.12,2.65-3.06.7-4.87a7.78,7.78,0,0,1-2-9.22c1.46-3.4,1.33-5.05-1-6.15-1.19-.56-2.78-.29-4.19-.39-.47,0-.93-.05-1.65-.09,1.67-3,3-5.78,6.86-6.28a4.12,4.12,0,0,0,3.2-2.73c.72-1.68,2.44-2.95,3.74-4.38,1.14-1.23,1.82-2.81.52-4a5.8,5.8,0,0,0-4-1A30.06,30.06,0,0,0,44.62,459c-1.84-6.08-.47-11.9,1.17-17.65A3.74,3.74,0,0,1,48,439.05c7.2-1.55,10.53-7,13.68-12.85a30.35,30.35,0,0,1,3.83-4.91c1.69-2,2.72-4.14,1.8-6.84s-1.71-5.51-2.66-8.23A52.51,52.51,0,0,0,62,399.51c-1.34-2.62-.9-5.19-.39-7.83q2.15-11.08,4.26-22.15c1.08-5.61,2.21-11.21,3.21-16.84a9.48,9.48,0,0,0-.36-2.74c-.16-1.32-.33-2.65-.44-4-.14-1.58-.18-3.17-.36-4.74a11.92,11.92,0,0,1,1.16-7.34c1.92-3.53,1.66-7.9-.48-9.87l1.06-1.21c7.65,6.68,15.29,13.38,23.2,19.74,8.69,7,17.59,13.76,26.79,20,13.27,9,26.71,17.88,40.42,26.24C169.85,394.8,180.18,399.83,190.26,405.28Z" transform="translate(-0.06 -0.26)" />
<path class="cls-4" d="M67.71,323.4c-4.2-2.13-8.84-4.21-13.43-1.55-2.84,1.64-5.18.87-8.06.13,2.21-2.52,1.9-5.53,1.88-8.49s0-6.08,0-9.44a8.14,8.14,0,0,1,1.68.85c5.42,4.9,10.73,9.92,16.24,14.72l3.65,3.2L68.61,324A4,4,0,0,0,67.71,323.4Z" transform="translate(-0.06 -0.26)" />
<path class="cls-1" d="M52.34,580.17c.15,1.23-.68,2.57-1.25,4.47-2.23-1.77-4.17-2.89-5.53-4.5a75.63,75.63,0,0,1-5.85-8.23,3.32,3.32,0,0,1-.2-2.75c2.52-5.09,2.08-6.95-3.06-9.61-1.82-.94-2.5-1.94-2.32-4,.32-3.52.35-4.15-4.79-9.94,2-2.92,1.55-3.82-.24-7-1.32-2.35-1.69-5.31-2.07-8.06-.29-2.13-.63-4-2.58-5.25a2.26,2.26,0,0,1-.79-1.75c.24-3.45.6-6.89.93-10.33,2-.08,2.8,1.11,3.33,2.71A29.72,29.72,0,0,0,36,529,45.65,45.65,0,0,1,43,536.54c1.16,1.66,1.53,4.06,1.7,6.17a84.4,84.4,0,0,1,0,10.06c-.17,4.12,1.42,7.63,3.38,11.07a18,18,0,0,1,2.24,4.56A109.17,109.17,0,0,1,52.34,580.17Z" transform="translate(-0.06 -0.26)" />
<path class="cls-1" d="M17.57,426.81l6.17,8.48C16.39,441.78,11.81,449.73,11,459.7,10.41,448.18,12.87,437.39,17.57,426.81Z" transform="translate(-0.06 -0.26)" />
</g>
</g>
<g id="Alberta" data-name="Alberta">
<path class="cls-5" d="M194.56,407.63c27.17,14.46,55.83,25.35,85,35.57L214.1,643.67c-10.88-4-21.29-7.82-31.68-11.68-3.52-1.3-6.91-3-10.51-4s-5.08-3.41-6.18-6.74c-1.26-3.84-.43-7.18,1.12-10.59A26.88,26.88,0,0,0,169.13,597a4.52,4.52,0,0,0-1-2.58c-4.16-4.42-5.32-9.88-5.71-15.66-.07-1-.13-2.32-.75-2.88-2.84-2.56-2.45-6.26-3.65-9.38-.64-1.69-1.62-3.26-2.29-4.95-.84-2.14-1.52-4.35-2.27-6.53-.05-.15-.06-.35-.17-.44a6.25,6.25,0,0,1-2-7.07c.17-.6-1.07-1.75-1.85-2.43a2.33,2.33,0,0,1-1.08-2.43,28.92,28.92,0,0,0-.52-7.52c-.36-2.17,0-4.65-2.15-6.28-1.47-1.09-1.92-2.71-2.35-4.49-.35-1.45-1.6-2.69-2.47-4-.51-.77-1.07-1.51-1.72-2.42,2.55-1.41,2.87-3.59,2.69-6a5,5,0,0,1,.5-2.5q5-9.88,10-19.7c6.78-13.11,13.63-26.19,20.37-39.32,3.84-7.45,7.51-15,11.31-22.47C187.5,421.2,191,414.48,194.56,407.63Z" transform="translate(-0.06 -0.26)" />
</g>
</svg>
</div>
Here's how the full map looks: https://jsfiddle.net/darkosss/7umphzwn/

Javscript to dynamically build HTML/SVG for mobile view

I have a full screen SVG image/mask reveal; it works great on desktop but it doesn't scale/fit on mobile devices. So im guessing the best solution would be to dynamically build the SVG and SVG container depending on screen width and have a mobile & desktop build. So i'm looking for advice and the best solution and if someone could point me in the right direction or know of an example I can look at.
// DRAW SVG MASK /////////////////////////////
var svg = document.querySelector("#svg__bg");
var tl = new gsap.timeline({ onUpdate: onUpdate });
var pt = svg.createSVGPoint();
var ratio = 0.5625;
tl.to("#masker", {duration: 2, attr: {r: 2400}, ease: "power2.in" });
tl.reversed(true);
function mouseHandler() {
tl.reversed(!tl.reversed());
}
function getPoint(evt) {
pt.x = evt.clientX;
pt.y = evt.clientY;
return pt.matrixTransform(svg.getScreenCTM().inverse());
}
function mouseMove(evt) {
var newPoint = getPoint(evt);
gsap.set("#dot", { cx: newPoint.x, cy: newPoint.y });
gsap.to("#ring, #masker", {duration: 0.88, attr: { cx: newPoint.x, cy: newPoint.y }, ease: "power2.out" });
}
function onUpdate() {
var prog = (tl.progress() * 100);
}
function newSize() {
var w = window.innerWidth;
var h = window.innerHeight;
if (w > h * (16 / 9)) {
gsap.set("#svg__bg", { attr: { width: w, height: w * ratio } });
} else {
gsap.set("#svg__bg", {
attr: {
width: h / ratio,
height: h
}
});
}
var data = svg.getBoundingClientRect();
gsap.set("#svg__bg", {
x: w / 2 - data.width / 2
});
gsap.set("#svg__bg", {
y: h / 2 - data.height / 2
});
}
window.addEventListener("mousedown", mouseHandler);
window.addEventListener("mouseup", mouseHandler);
window.addEventListener("mousemove", mouseMove);
newSize();
window.addEventListener("resize", newSize);
#import url('https://fonts.googleapis.com/css?family=Montserrat:700&display=swap');
body {
min-height: 100vh;
background-color: #1F242D;
cursor: none;
overflow: hidden;
font-family: 'Montserrat', sans-serif;
}
.intro-svg {
position: relative;
padding: 0;
margin: 0;
width: 100%;
min-height: 100vh;
height: calc(var(--vh, 1vh) * 100);
overflow: hidden;
z-index: 1;
}
.svg__container {
height: 100%;
width: 100%;
position: relative;
overflow: hidden;
}
.svg__image {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
cursor: none;
}
.text-svg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
cursor: none;
z-index:2;
}
.impact-text {
font-size: 7em;
line-height: 1.2;
text-transform: uppercase;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.6/gsap.min.js"></script>
<div class="intro-svg">
<div class="svg__container">
<svg id="svg__bg" class="svg__image" xmlns="http://www.w3.org/2000/svg" x="0" y="0" width="1600" height="900" viewBox="0 0 1600 900">
<defs>
<radialGradient id="mask-gradient">
<stop offset="80%" stop-color="#fff"/><stop offset="100%" stop-color="#000" />
</radialGradient>
<mask id="the-mask">
<circle id="masker" r="250" fill="url(#mask-gradient)" cx="800" cy="450">
</circle>
</mask>
<mask id="mask-text" width="100" height="100" x="0" y="0">
<text id="masker" class="impact-text row-1" fill="none" stroke="#fff" stroke-width="3" x="10.1%" y="42%">A</text>
<text id="masker" class="impact-text row-2" fill="white" x="10%" y="55%">Digital</text>
<text id="masker" class="impact-text row-3" fill="white" x="10%" y="68%">Designer</text>
</mask>
</defs>
<image id="lines" xlink:href="https://i.imgur.com/1TQRj56.jpg" x="0" y="0" width="1600" height="900" />
<g id="mask-reveal" mask="url(#the-mask)">
<image id="regular" xlink:href="https://i.imgur.com/7VtEKv3.jpg" x="0" y="0" width="1600" height="900" />
</g>
<g mask="url(#mask-text)">
<image id="text-before" xlink:href="https://i.imgur.com/7VtEKv3.jpg" x="0" y="0" width="1600" height="900" />
</g>
<circle id="ring" r="20" fill="none" stroke="#D74A53" stroke-width="2" cx="800" cy="450" />
<circle id="dot" r="4" fill="#D74A53" cx="800" cy="450"/>
</svg>
</div>
</div>
just add
#svg__bg {
width: 100%;
height:auto;
}
andchange them with media query
don't forget to change
<svg id="svg__bg" class="svg__image" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 1600 900">
without width="1600" height="900"

Categories