For quite a while I have been dealing with this scenario where I want to zoom in on an SVG image by clicking on it (or tapping in case of a touch-screen device), having it so that where you have clicked stays in the same position on the page while zooming in.
What happens at the moment is that when zooming is done, the pivot is not the clicked point but another point. It seems I cannot get to think of the correct formula to translate the svg as zooming in is done. Preferably I would see a solution that doesn't use an external library as the code below is a simplified version of a bigger project that has quite complicated stuff that would propably not be as easy to refactor to another component.
let zoomedIn = false;
let svgViewboxWidth, svgViewboxHeight;
let factor = 1;
function init() {
svgViewboxWidth = 335.2262416700105;
svgViewboxHeight = 304.65863467997406;
let svgContainer = document.getElementById('svgContainer');
svgContainer.addEventListener('click', svgClick);
}
function svgClick(event) {
let newCalcX = 28, newCalcY = -86;
let gZoom = document.getElementById('zoomscale');
if (zoomedIn) {
// zoom out
factor = 1;
newCalcX = 0;
newCalcY = 0;
zoomedIn = false;
} else {
// zoom in
factor = 3;
newCalcX = (event.clientX - 86) * -1;
newCalcY = (event.clientY + 28) * -1;
zoomedIn = true;
}
let transformValue = `translate(${newCalcX}, ${newCalcY}) scale(${factor})`;
gZoom.setAttribute('transform', transformValue);
}
body {
margin: 0px;
padding: 0px;
}
.container {
width: 100%;
height: 100%;
}
<?xml version='1.0' encoding='UTF-8'?>
<html>
<head>
<title>SVG zoom issue</title>
</head>
<body onload="init()">
<div id="svgContainer" class="container">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="view" height="100%" width="100%" viewBox="0 0 335.2262416700105 304.65863467997406" preserveAspectRatio="xMidYMid meet" version="1.1" class="view">
<style>svg { background-color:rgba(64, 64, 64, 0.1); }</style>
<g id="zoomscale">
<line x1="20" x2="310" y1="150" y2="150" style="stroke: #000000; stroke-width: 1px" />
<line x1="165" x2="165" y1="20" y2="280" style="stroke: #000000; stroke-width: 1px" />
<circle cx="165" cy="150" r="20" style="stroke: #000000; stroke-width: 1px; fill: none" />
<circle cx="165" cy="150" r="40" style="stroke: #000000; stroke-width: 1px; fill: none" />
<circle cx="165" cy="150" r="60" style="stroke: #000000; stroke-width: 1px; fill: none" />
<circle cx="165" cy="150" r="90" style="stroke: #000000; stroke-width: 1px; fill: none" />
<circle cx="165" cy="150" r="120" style="stroke: #000000; stroke-width: 1px; fill: none" />
</g>
</svg>
</div>
</body>
<html>
I feel like I am missing something obvious, or maybe I am just not able to calculate the correct translate- values for zooming. Massive thanks to the one(s) who can help me with this!
hwllo i want my circle counters to display counter value as i mention in their class and data-percent
but currently all 4 counters are shwoing the data of 1st counter only however i have mentioned the data-percent value of each counter in its html code.
please help me figure out what's wrong and what should i do to make it correct?
any help is appreciated and code is provided for clear understanding:
the code used below is showing me this result as shown in the picture:
currently i'm using this js code snippet
<script type="text/javascript">
(function () {
var bar, c, percent, r, range;
percent = $('.pie').data('percent');
bar = $('.pie').find('.bar');
r = bar.attr('r');
c = Math.PI * (r * 2);
range = (100 - percent) / 100 * c;
bar.css({
'stroke-dashoffset': c,
'stroke-dasharray': c });
bar.animate({
strokeDashoffset: range },
1000, 'linear');
$('.text').prop('Counter', 0).animate({
Counter: percent },
{
duration: 1000,
step: function (now) {
return $(this).text(Math.ceil(now) + '%');
} });
}).call(this);
</script>
and html is this:
<!--counter new -->
<div class="container-fluid counter">
<div class="row">
<div class="col-lg-3">
<div class="svg-box"><span class="text">0%</span>
<svg class="pie" viewbox="0 0 200 200" data-percent="99">
<circle r="90" cx="100" cy="100"></circle>
<circle class="bar" r="90" cx="100" cy="100"></circle>
</svg>
</div>
</div>
<div class="col-lg-3">
<div class="svg-box"><span class="text">0%</span>
<svg class="pie" viewbox="0 0 200 200" data-percent="32">
<circle r="90" cx="100" cy="100"></circle>
<circle class="bar" r="90" cx="100" cy="100"></circle>
</svg>
</div>
</div>
<div class="col-lg-3">
<div class="svg-box"><span class="text">0%</span>
<svg class="pie" viewbox="0 0 200 200" data-percent="44">
<circle r="90" cx="100" cy="100"></circle>
<circle class="bar" r="90" cx="100" cy="100"></circle>
</svg>
</div>
</div>
<div class="col-lg-3">
<div class="svg-box"><span class="text">0%</span>
<svg class="pie" viewbox="0 0 200 200" data-percent="22">
<circle r="90" cx="100" cy="100"></circle>
<circle class="bar" r="90" cx="100" cy="100"></circle>
</svg>
</div>
</div>
</div>
</div>
<!--counter new end -->
the css used is also shown below:
.svg-box {
position: relative;
}
.text {
position: absolute;
top: 50%;
left: 40%;
transform: translate(-50%, -50%);
z-index: 1;
font-size: 3em;
letter-spacing: 3px;
font-family: "Raleway", Calibri, sans-serif;
color: #fff
}
svg {
transform: rotate(-90deg);
width: 200px;
height: 200px;
background: transparent;
fill: none;
}
svg circle {
stroke-width: 1;
stroke: #999;
}
svg .bar {
stroke-width: 3;
stroke: #ffffff;
stroke-linecap: round;
}
i want to display the counters by showing the
"data-percent" vale
whihc i've mentioned in the html code. but all the counters are showing the data value of 1st counter.
how ever what i want to display data is like this:
I've redesigned a little your code, and I think it should work correctly.
(function () {
$('.pie').each(function(){
let percent = $(this).data('percent');
let bar = $(this).find('.bar');
let r = bar.attr('r');
let c = Math.PI * (r * 2);
let range = (100 - percent) / 100 * c;
bar.css({
'stroke-dashoffset': c,
'stroke-dasharray': c
});
bar.animate({ strokeDashoffset: range }, 1000, 'linear');
$(this).prev().prop('Counter', 0).animate({
Counter: percent
}, {
duration: 1000,
step: function (now) {
return $(this).text(Math.ceil(now) + '%');
}
});
})
}).call(this);
And here is a working fiddle: https://jsfiddle.net/w2o7rt1L/
I recently discover the CSS resize:
`
div {
resize: both;
overflow: auto;
}
`
There is also a polyFill, but none of them won't work for a SVG element :/
Is it possible at least ?
Starting from your code and #ChrisG's comment, you might do the following.
When one clicks on svg, wrap this svg into a resizable div and set the svg width and height attributes to '100%' so the svg would get the wrapper's size.
Then, when one resizes the div, the svg would be resized as well.
When one clicks out of the div, set the svg width and height attributes explicitly and remove the div.
See the snippet below:
window.addEventListener('DOMContentLoaded', () => {
document.addEventListener('click', (e) => {
var r = document.getElementById('r');
if (r && r != e.target) {
var svg = r.querySelector('svg');
svg.setAttribute('width', r.offsetWidth + 'px');
svg.setAttribute('height', r.offsetHeight + 'px');
r.replaceWith(svg);
}
var svg = e.target;
while (!!svg && svg.nodeName != 'svg')
svg = svg.parentElement;
if (!svg) return;
var r = document.createElement('div');
r.id = 'r';
r.style.width = svg.width.baseVal.valueAsString;
r.style.height = svg.height.baseVal.valueAsString;
svg.setAttribute('width', '100%');
svg.setAttribute('height', '100%');
svg.parentElement.insertBefore(r, svg);
r.appendChild(svg.parentElement.removeChild(svg));
});
});
svg {
background: #cef
}
#r {
display: inline-block;
box-sizing: border-box;
border: solid 1px;
resize: both;
overflow: hidden;
}
<svg width="100px" height="100px" viewBox="0 0 100 100">
<circle stroke="navy" stroke-width="5" fill="pink" cx="50" cy="50" r="40"/>
</svg>
<svg width="100px" height="100px" viewBox="0 0 100 100">
<circle stroke="green" stroke-width="5" fill="gold" cx="50" cy="50" r="40"/>
</svg>
In html, I have a button element with no text. It has a child svg element with some paths and rectangles. It works fine:
I try to create this in javascript. The problem is, that the button is not visible. If I set some text to it with textContent or innerHtml, the button is visible with the text, but the svg is not there. How can I create this button in javascript? This is the code:
var myButton = document.createElement("button");
myButton.setAttribute("class", "my-button");
myButton.setAttribute("id", "foo");
var mySVG = document.createElement("svg");
mySVG.setAttribute("id", "my-svg");
mySVG.setAttribute("viewBox", "0 0 12.25 15.45");
var icon1 = document.createElement("g");
icon1.setAttribute("class", "g-element1");
icon1.setAttribute("id", "g1");
var iconPath = document.createElement("path");
iconPath.setAttribute("d", "M0,25L0,0l12.25,7.7L0,15.45z");
var icon2 = document.createElement("g");
icon2.setAttribute("class", "g-element2");
icon2.setAttribute("id", "g2");
var rect1 = document.createElement("rect");
rect1.setAttribute("x", "0");
rect1.setAttribute("y", "0");
rect1.setAttribute("width", "4.1");
rect1.setAttribute("height", "15.45");
var rect2 = document.createElement("rect");
rect2.setAttribute("x", "8.1");
rect2.setAttribute("y", "0");
rect2.setAttribute("width", "4.1");
rect2.setAttribute("height", "15.45");
icon1.appendChild(iconPath);
icon2.appendChild(rect1);
icon2.appendChild(rect2);
mySVG.appendChild(icon1);
mySVG.appendChild(icon2);
myButton.appendChild(mySVG);
document.getElementById('some-element').appendChild(myButton)
.my-button {
font-size: 14px;
height: 17px;
cursor: pointer;
margin-left: 5px;
&:hover, &:focus {
opacity: .8;
}
}
<div id="some-element">
<button class="my-button" id="foo">
<svg id="my-svg" viewBox="0 0 12.25 15.45">
<g class="g-element1" id="g1">
<path d="M0,25L0,0l12.25,7.7L0,15.45z"/>
</g>
<g class="g-element2" id="g2">
<rect x="0" y="0" width="4.1" height="15.45"/>
<rect x="8.1" y="0" width="4.1" height="15.45"/>
</g>
</svg>
</button>
</div>
Also when I create just the button in javascript and I set no text to it (and no svg either), the button is not visible.
When creating SVG elements (including elements inside the the SVG tage) using JavaScript, you need to use document.createElementNS(namespaceURI, qualifiedName) with the appropriate namespace URI http://www.w3.org/2000/svg. You also need to assign a height to the SVG element.
Because you have to use the namespace for every element that you're creating within the SVG tag as well as the SVG tag itself, you may want to curry the function to save space and prevent typos:
const createSVGElement = qn => document.createElementNS("http://www.w3.org/2000/svg", qn);
Here's your code fixed:
var myButton = document.createElement("button");
myButton.setAttribute("class", "my-button");
myButton.setAttribute("id", "foo");
const createSVGElement = qn => document.createElementNS("http://www.w3.org/2000/svg", qn);
var mySVG = createSVGElement("svg");
mySVG.setAttribute("id", "my-svg");
mySVG.setAttribute('height', "14px");
mySVG.setAttribute("viewBox", "0 0 12.25 15.45");
var icon1 = createSVGElement("g");
icon1.setAttribute("class", "g-element1");
icon1.setAttribute("id", "g1");
var iconPath = createSVGElement("path");
iconPath.setAttribute("d", "M0,25L0,0l12.25,7.7L0,15.45z");
var icon2 = createSVGElement("g");
icon2.setAttribute("class", "g-element2");
icon2.setAttribute("id", "g2");
var rect1 = createSVGElement("rect");
rect1.setAttribute("x", "0");
rect1.setAttribute("y", "0");
rect1.setAttribute("width", "4.1");
rect1.setAttribute("height", "15.45");
var rect2 = createSVGElement("rect");
rect2.setAttribute("x", "8.1");
rect2.setAttribute("y", "0");
rect2.setAttribute("width", "4.1");
rect2.setAttribute("height", "15.45");
icon1.appendChild(iconPath);
icon2.appendChild(rect1);
icon2.appendChild(rect2);
mySVG.appendChild(icon1);
mySVG.appendChild(icon2);
myButton.appendChild(mySVG);
document.getElementById("some-element").appendChild(myButton);
.my-button {
font-size: 14px;
height: 17px;
cursor: pointer;
margin-left: 5px;
&:hover, &:focus {
opacity: .8;
}
}
<div id="some-element">
<button class="my-button" id="foo">
<svg id="my-svg" viewBox="0 0 12.25 15.45" height="14px">
<g class="g-element1" id="g1">
<path d="M0,25L0,0l12.25,7.7L0,15.45z"/>
</g>
<g class="g-element2" id="g2">
<rect x="0" y="0" width="4.1" height="15.45"/>
<rect x="8.1" y="0" width="4.1" height="15.45"/>
</g>
</svg>
</button>
</div>
The SVG appears to be collapsing to zero width and height inside the button. You can prevent this by setting an explicit width and height on it:
.my-button {
font-size: 14px;
height: 17px;
cursor: pointer;
margin-left: 5px;
&:hover, &:focus {
opacity: .8;
}
}
#my-svg {width: 100%; height: 100%}
<div id="some-element">
<button class="my-button" id="foo">
<svg id="my-svg" viewBox="0 0 12.25 15.45">
<g class="g-element1" id="g1">
<path d="M0,25L0,0l12.25,7.7L0,15.45z"/>
</g>
<g class="g-element2" id="g2">
<rect x="0" y="0" width="4.1" height="15.45"/>
<rect x="8.1" y="0" width="4.1" height="15.45"/>
</g>
</svg>
</button>
</div>
The same should apply whether the SVG is defined inline or script-generated. But note that when generating non-HTML nodes it's necessary to use .createElementNS() and include the namespace, as below:
var myButton = document.createElement("button");
myButton.setAttribute("class", "my-button");
myButton.setAttribute("id", "foo");
var svgns = "http://www.w3.org/2000/svg";
var mySVG = document.createElementNS(svgns, "svg");
mySVG.setAttribute("id", "my-svg");
mySVG.setAttribute("viewBox", "0 0 12.25 15.45");
var icon1 = document.createElementNS(svgns, "g");
icon1.setAttribute("class", "g-element1");
icon1.setAttribute("id", "g1");
var iconPath = document.createElementNS(svgns, "path");
iconPath.setAttribute("d", "M0,25L0,0l12.25,7.7L0,15.45z");
var icon2 = document.createElementNS(svgns, "g");
icon2.setAttribute("class", "g-element2");
icon2.setAttribute("id", "g2");
var rect1 = document.createElementNS(svgns, "rect");
rect1.setAttribute("x", "0");
rect1.setAttribute("y", "0");
rect1.setAttribute("width", "4.1");
rect1.setAttribute("height", "15.45");
var rect2 = document.createElementNS(svgns, "rect");
rect2.setAttribute("x", "8.1");
rect2.setAttribute("y", "0");
rect2.setAttribute("width", "4.1");
rect2.setAttribute("height", "15.45");
icon1.appendChild(iconPath);
icon2.appendChild(rect1);
icon2.appendChild(rect2);
mySVG.appendChild(icon1);
mySVG.appendChild(icon2);
document.getElementById('some-element').appendChild(mySVG)
#my-svg {
width: 100%;
height: 100%
}
button {height: 14px}
<button id="some-element"></div>
Setting the svg element as innerHTML to the button might give you the result.
document.getElementById('foo').innerHTML = '<svg id="my-svg" viewBox="0 0 12.25 15.45"><g class="g-element1" id="g1"><path d="M0,25L0,0l12.25,7.7L0,15.45z"/></g><g class="g-element2" id="g2"><rect x="0" y="0" width="4.1" height="15.45"/><rect x="8.1" y="0" width="4.1" height="15.45"/></g></svg>';
.my-button {
font-size: 14px;
height: 17px;
cursor: pointer;
margin-left: 5px;
&:hover, &:focus {
opacity: .8;
}
}
svg
{
height:100%;
width:100%;
}
<div id="some-element">
<button class="my-button" id="foo">
</button>
</div>
Your code is actually Ok but you will need a way to add the button to the DOM for example if you are to append it to the body, You may have to do something like
document.body.appendChild(playButton)
You can as well add it to an existing element on the webpage
So I have this bit of code for two SVG arrows that squint when clicked on (via Snap.svg) and I want to have one of them move up and down along the y axis in a loop to make it look like it's hovering while the other one is active (clicked upon).
The Snap documentation has a bit about translating matrices but has nothing on animating them. I'm assuming this is possible with Snap (some way or other), so how is it done?
var suspend = true;
Array.prototype.slice.call(document.querySelectorAll('.arrow_button')).forEach(function(el) {
var s = Snap(el.querySelector('svg')),
path = s.select('path'),
squint = el.getAttribute('data-path-squint'),
resetPath = path.attr('d'),
callback = function() {
path.animate({
'path': resetPath
}, 1300, mina.elastic);
setTimeout(function() {
suspend = true;
}, 150)
};
el.addEventListener('click', function() {
if (suspend) {
path.animate({
'path': squint
}, 100, mina.linear, callback);
suspend = false;
};
});
});
.arrow_button {
width: 55px;
height: 79.8px;
position: fixed;
top: 50%;
margin-top: -40.5px;
cursor: pointer;
}
.arrow_styles {
fill: none;
stroke: #000;
stroke-width: 10;
stroke-linecap: round;
}
.left {
left: 7px;
}
.right {
right: 7px;
}
<script src="https://cdn.jsdelivr.net/snap.svg/0.4.1/snap.svg-min.js"></script>
<div class="left arrow_button" onclick="" preserveAspectRatio="none" data-path-squint="M46.2,16.1L14,35.5C8.4,40,8.6,47,14,51.1l32.2,19.6">
<svg id="left_arrow" class="arrow_styles" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 60 86.8">
<path d="M43.7,8.1L20.2,33.2c-5.1,5.5-5.1,14.6,0,20.1l23.5,25.3" />
</svg>
</div>
<div class="right arrow_button" onclick="" preserveAspectRatio="none" data-path-squint="M9.9,16.1l32.2,19.4c5.6,4.5,5.5,11.5,0,15.5L9.9,70.6">
<svg id="right_arrow" class="arrow_styles" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 60 86.8">
<path d="M16.3,8.1l23.5,25.1c5.1,5.5,5.1,14.6,0,20.1L16.3,78.6" />
</svg>
</div>