JQuery position() returns zeros in Safari - javascript

I use position() and getBoundingClientRect() to determine if svg object is visible in viewport. Works perfectly well in Chrome, Firefox and EDGE but not in Safari.
obj.position() always returns {top: 0, left: 0} in Safari.
What could be wrong?
options.elements.forEach(function (item) {
let obj = jQuery("#" + item);
if (!!obj && !!obj[0]) {
let position = obj.position();
let box = obj[0].getBoundingClientRect()
if (!!position) {
if (position.left <= left + width &&
position.left + box.width >= left &&
position.top <= top + height &&
position.top + box.height >= top)
onscreen.push(item);
}
}
});
Here is part of HTML. I omitted contents of 'd' of svg
<div id="svgmap">
<svg id="svg">
<g id="R918">
<path d="...." transform="translate(-0.08 0.25)"//>
</g>
<g id="C902J">
<path d="...." transform="translate(-0.08 0.25)"/>
</g>
<g id="C9002I">
<path d="" transform="translate(-0.08 0.25)"/>
</g>
</svg>

Ok, so I didn't figure out why position() is not working in Safari in this particular case but I realized that I don't need to user position() at all. I can get the same data from getBoundingClientRect().left and getBoundingClientRect().top

Related

How do i set position of scroll trigger on SVG?

I have a SVG line that triggers on scroll and goes down the page as i scroll. I want the trigger to be at a specific location on the page, but for now, even though the SVG is located further down the page, it starts "growing" as soon as the user starts scrolling (which result in no animation when i arrive at that location, since the SVG is already all scrolled out).
Here's the js of that particular SVG:
var path = document.querySelector('#star-path');
var pathLength = path.getTotalLength();
path.style.strokeDasharray = pathLength + ' ' + pathLength;
path.style.strokeDashoffset = pathLength;
window.addEventListener('scroll', () => {
var scrollPercentage = (document.documentElement.scrollTop + document.body.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight);
var drawLength = pathLength * scrollPercentage;
path.style.strokeDashoffset = pathLength - drawLength;
});
And here is the html snippet...
<svg class="svg" width="10" viewBox="0 0 2 150" id="star-svg">
<path id="star-path" fill="none" stroke="black" stroke-width="2" d="M1 0V150" />
</svg>
... as well as the css:
.svg {
position: absolute;
top: 2900px;
left: 1400px;
text-align: center;
overflow: visible;
}
Which element corresponds to that trigger, and what should i do to "delay" it (i am quite new at SVG animations)?
Thanks in advance for your help

Unable to have multiple SVG text paths animate?

I’m trying to get multiple headings on a homepage to be animated on a svg textPath, each is shown for 3 breakpoints one for Desktop, one for Tablet, one for Mobile. My code loads the headings and they each sit on their own svg curved path.
However, only one heading is animated on scroll, the others are just frozen when I change the breakpoint, I've even tried loading them all on the same page with no media queries and only still one animates and the rest are still.
How do I get each of the headings to be animated on scroll?
I tried changing out the variables like suggested here but not it's working.
SVG textPath animation on Scroll multiple times on a page
But not sure why it's not working.
They're all the same code just the variables changed, added a number to each.
Thanks
This is the first heading for Desktop.
<path id="text-curve" d="M0 100s269.931 86.612 520 0c250.069-86.612 480 0 480 0" fill="none"/>
<text y="40" font-size="3em">
<textPath id="text-path" href="#text-curve" startOffset="200">
This is a heading</textPath>
</text>
</svg>
<script>
console.clear();
var textPath = document.querySelector('#text-path');
var textContainer = document.querySelector('#text-container');
var path = document.querySelector( textPath.getAttribute('href') );
var pathLength = path.getTotalLength();
console.log(pathLength);
function updateTextPathOffset(offset){
textPath.setAttribute('startOffset', offset);
}
updateTextPathOffset(pathLength);
function onScroll(){
requestAnimationFrame(function(){
var rect = textContainer.getBoundingClientRect();
var scrollPercent = rect.y / window.innerHeight;
console.log(scrollPercent);
updateTextPathOffset( scrollPercent * 2 * pathLength );
});
}
window.addEventListener('scroll',onScroll);
</script>
This is the 2nd heading for Tablet.
<svg id="text-container2" viewBox="0 -300 1000 500" xmlns="http://www.w3.org/2000/svg">
<path id="text-curve2" d="M0 100s269.931 86.612 520 0c250.069-86.612 480 0 480 0" fill="none"/>
<text y="40" font-size="3em">
<textPath id="text-path2" href="#text-curve2" startOffset="200">
This is a sub-heading</textPath>
</text>
</svg>
<script>
console.clear();
var textPath2 = document.querySelector('#text-path2');
var textContainer2 = document.querySelector('#text-container2');
var path = document.querySelector( textPath.getAttribute('href') );
var pathLength = path.getTotalLength();
console.log(pathLength);
function updateTextPathOffset(offset){
textPath.setAttribute('startOffset', offset);
}
updateTextPathOffset(pathLength);
function onScroll(){
requestAnimationFrame(function(){
var rect = textContainer.getBoundingClientRect();
var scrollPercent = rect.y / window.innerHeight;
console.log(scrollPercent);
updateTextPathOffset( scrollPercent * 2 * pathLength );
});
}
window.addEventListener('scroll',onScroll);
</script>
This is the 3rd heading for Mobile.
<svg id="text-container3" viewBox="0 -1000 1000 1200" xmlns="http://www.w3.org/2000/svg">
<path id="text-curve3" d="M0 100s269.931 86.612 520 0c250.069-86.612 480 0 480 0" fill="none"/>
<text y="40" font-size="3em">
<textPath id="text-path3" href="#text-curve2" startOffset="200">
This is a 3rd heading</textPath>
</text>
</svg>
<script>
console.clear();
var textPath3 = document.querySelector('#text-path3');
var textContainer3 = document.querySelector('#text-container3');
var path = document.querySelector( textPath.getAttribute('href') );
var pathLength = path.getTotalLength();
console.log(pathLength);
function updateTextPathOffset(offset){
textPath.setAttribute('startOffset', offset);
}
updateTextPathOffset(pathLength);
function onScroll(){
requestAnimationFrame(function(){
var rect = textContainer.getBoundingClientRect();
var scrollPercent = rect.y / window.innerHeight;
console.log(scrollPercent);
updateTextPathOffset( scrollPercent * 2 * pathLength );
});
}
window.addEventListener('scroll',onScroll);
</script>

Animate SVG viewBox change

I am currently using velocity.js to control some animation on an SVG drawing I am creating. It is interactive with the user, so when a click happens in certain parts, the picture might grow to the right or down. So far everything is working perfectly until the picture gets too big for the SVG box.
In those situations, I simply resize the viewBox to scale the image.
svgDoc.setAttribute("viewBox", "0 0 " + svgDocWidth + " " + svgDocHeight);
This works, but it doesn't look great because it isn't animated. It just jumps to the new size. Is there a way to animate with velocity.js a viewBox change?
I've tried this approach:
$viewBox = $(svgDoc.viewBox);
$viewBox.velocity({height: svgDocHeight, width: svgDocWidth});
But that doesn't do anything.
Is this beyond what velocity.js can support?
Solution on 2015-11-21
#Ian gave the solution I eventually used. It ended up looking like this:
var origHeight = this.svgDoc.viewBox.animVal.height;
var origWidth = this.svgDoc.viewBox.animVal.width;
var docHeight = this.SvgHeight();
var docWidth = this.SvgWidth();
if ((origHeight != docHeight) || (origWidth != docWidth))
{
var deltaHeight = docHeight - origHeight;
var deltaWidth = docWidth - origWidth;
$(this.svgDoc).velocity(
{
tween: 1
},
{
progress: function(elements, complete, remaining, start, tweenValue)
{
var newWidth = origWidth + complete * deltaWidth;
var newHeight = origHeight + complete * deltaHeight;
elements[0].setAttribute('viewBox', '0 0 ' + newWidth + ' ' + newHeight);
}
});
}
You can animate a viewBox smoothly via SMIL. Like this...
<svg width="100%" height="100%" viewBox="0 0 200 200">
<animate attributeName="viewBox" to="0 0 400 400" dur="5s" fill="freeze" />
<circle cx="100" cy="100" r="100" fill="green" />
</svg>
I think SMIL is being deprecated in Chrome (please correct me if I'm wrong), so I'm guessing there will be increasing reliance on other methods.
There's no reason you can't use velocity, but with your own calculations and the progress/tweenValue parameter, eg something a bit like this...
$("#mysvg").velocity(
{ tween: 200 },
{ progress: animViewbox }
)
function animViewbox (elements, complete, remaining, start, tweenValue) {
elements[0].setAttribute('viewBox', '0 0 ' + tweenValue + ' ' + tweenValue);
}
jsfiddle

Dragging SVG element over another SVG element

Is there any way i can drag an SVG element over another SVG element? I tried but like in this tutorial i can only drag the one i placed the second over the first one. There is no way i can drag first one over the second without problems.
Does anyone know how to solve this?
Here is the whole tutorial: http://www.petercollingridge.co.uk/book/export/html/437
I was writing it before I saw that #Strat-O gave you the same approach.
So here is a commented example of that :
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="400" height="200">
<style>
.draggable {
cursor: move;
}
</style>
<script type="text/ecmascript"><![CDATA[
var selectedElement = 0;
var currentX = 0;
var currentY = 0;
var currentMatrix = 0;
function cloneToTop(oldEl){
// already at top, don't go farther…
if(oldEl.atTop==true) return oldEl;
// make a copy of this node
var el = oldEl.cloneNode(true);
// select all draggable elements, none of them are at top anymore
var dragEls= oldEl.ownerDocument.documentElement.querySelectorAll('.draggable');
for(i=0; i<dragEls.length; i++){
dragEls[i].atTop=null;
}
var parent = oldEl.parentNode;
// remove the original node
parent.removeChild(oldEl);
// insert our new node at top (last element drawn is first visible in svg)
parent.appendChild(el);
// Tell the world that our new element is at Top
el.atTop= true;
return el;
}
function selectElement(evt) {
selectedElement = cloneToTop(evt.target);
currentX = evt.clientX;
currentY = evt.clientY;
currentMatrix = selectedElement.getAttributeNS(null, "transform").slice(7,-1).split(' ');
for(var i=0; i<currentMatrix.length; i++) {
currentMatrix[i] = parseFloat(currentMatrix[i]);
}
selectedElement.setAttributeNS(null, "onmousemove", "moveElement(evt)");
selectedElement.setAttributeNS(null, "onmouseout", "deselectElement(evt)");
selectedElement.setAttributeNS(null, "onmouseup", "deselectElement(evt)");
}
function moveElement(evt) {
var dx = evt.clientX - currentX;
var dy = evt.clientY - currentY;
currentMatrix[4] += dx;
currentMatrix[5] += dy;
selectedElement.setAttributeNS(null, "transform", "matrix(" + currentMatrix.join(' ') + ")");
currentX = evt.clientX;
currentY = evt.clientY;
}
function deselectElement(evt) {
if(selectedElement != 0){
selectedElement.removeAttributeNS(null, "onmousemove");
selectedElement.removeAttributeNS(null, "onmouseout");
selectedElement.removeAttributeNS(null, "onmouseup");
selectedElement = 0;
}
}
]]> </script>
<g>
<circle/>
</g>
<rect x="0.5" y="0.5" width="399" height="199" fill="none" stroke="black"/>
<rect class="draggable" id="blue" x="30" y="30" width="80" height="80" fill="blue" transform="matrix(1 0 0 1 46 18)" onmousedown="selectElement(evt)"/>
<rect class="draggable" id="green" x="160" y="50" width="50" height="50" fill="green" transform="matrix(1 0 0 1 51 11)" onmousedown="selectElement(evt)"/>
</svg>
Unfortunately, there is only one way to make an element appear in front of another element in SVG and that is to remove the lower element then turn around and redraw it. There is no z-index or other helpful attribute that you can set. I spent a bit of time on this and that is my conclusion.
There is one upside and that is by removing and redrawing it, it ensures that the display order of all of the elements is maintained whereas if you have to maintain z-indexes, managing the numbers can cause its own set of issues.

Get width/height of SVG element

What is the proper way to get the dimensions of an svg element?
http://jsfiddle.net/langdonx/Xkv3X/
Chrome 28:
style x
client 300x100
offset 300x100
IE 10:
stylex
client300x100
offsetundefinedxundefined
FireFox 23:
"style" "x"
"client" "0x0"
"offset" "undefinedxundefined"
There are width and height properties on svg1, but .width.baseVal.value is only set if I set the width and height attributes on the element.
The fiddle looks like this:
HTML
<svg id="svg1" xmlns="http://www.w3.org/2000/svg" version="1.1">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="1" fill="red" />
<circle cx="150" cy="50" r="40" stroke="black" stroke-width="1" fill="green" />
<circle cx="250" cy="50" r="40" stroke="black" stroke-width="1" fill="blue" />
</svg>
JS
var svg1 = document.getElementById('svg1');
console.log(svg1);
console.log('style', svg1.style.width + 'x' + svg1.style.height);
console.log('client', svg1.clientWidth + 'x' + svg1.clientHeight);
console.log('offset', svg1.offsetWidth + 'x' + svg1.offsetHeight);
CSS
#svg1 {
width: 300px;
height: 100px;
}
Use the getBBox function:
The SVGGraphicsElement.getBBox() method allows us to determine the coordinates of the smallest rectangle in which the object fits. [...]
http://jsfiddle.net/Xkv3X/1/
var bBox = svg1.getBBox();
console.log('XxY', bBox.x + 'x' + bBox.y);
console.log('size', bBox.width + 'x' + bBox.height);
FireFox have problemes for getBBox(), i need to do this in vanillaJS.
I've a better Way and is the same result as real svg.getBBox() function !
With this good post : Get the real size of a SVG/G element
var el = document.getElementById("yourElement"); // or other selector like querySelector()
var rect = el.getBoundingClientRect(); // get the bounding rectangle
console.log( rect.width );
console.log( rect.height);
I'm using Firefox, and my working solution is very close to obysky. The only difference is that the method you call in an svg element will return multiple rects and you need to select the first one.
var chart = document.getElementsByClassName("chart")[0];
var width = chart.getClientRects()[0].width;
var height = chart.getClientRects()[0].height;
SVG has properties width and height. They return an object SVGAnimatedLength with two properties: animVal and baseVal. This interface is used for animation, where baseVal is the value before animation. From what I can see, this method returns consistent values in both Chrome and Firefox, so I think it can also be used to get calculated size of SVG.
This is the consistent cross-browser way I found:
var heightComponents = ['height', 'paddingTop', 'paddingBottom', 'borderTopWidth', 'borderBottomWidth'],
widthComponents = ['width', 'paddingLeft', 'paddingRight', 'borderLeftWidth', 'borderRightWidth'];
var svgCalculateSize = function (el) {
var gCS = window.getComputedStyle(el), // using gCS because IE8- has no support for svg anyway
bounds = {
width: 0,
height: 0
};
heightComponents.forEach(function (css) {
bounds.height += parseFloat(gCS[css]);
});
widthComponents.forEach(function (css) {
bounds.width += parseFloat(gCS[css]);
});
return bounds;
};
From Firefox 33 onwards you can call getBoundingClientRect() and it will work normally, i.e. in the question above it will return 300 x 100.
Firefox 33 will be released on 14th October 2014 but the fix is already in Firefox nightlies if you want to try it out.
Use .getAttribute()!
var height = document.getElementById('rect').getAttribute("height");
var width = document.getElementById('rect').getAttribute("width");
var x = document.getElementById('rect').getAttribute("x");
alert("height: " + height + ", width: " + width + ", x: " + x);
<svg width="500" height="500">
<rect width="300" height="100" x="50" y="50" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" id="rect"/>
</svg>
A save method to determine the width and height unit of any element (no padding, no margin) is the following:
let div = document.querySelector("div");
let style = getComputedStyle(div);
let width = parseFloat(style.width.replace("px", ""));
let height = parseFloat(style.height.replace("px", ""));

Categories

Resources