jaepage jaepage - 6 months ago 12
Javascript Question

Animated SVG prevents D3 from transitioning `top` style of separate divs in iOS only

My code works properly on Mac (Mavericks) Chrome, Safari, and Firefox, but if fails on iOS7 Chrome and Safari.

On the desktop versions, an SVG with a image-pattern background fills the window and scrolls downward indefinitely, while D3 adds divs to the DOM and transitions their

top
positions to the window's bottom on a
setInterval
.

The animation of the falling blue div fails to occur on mobile.

If the SVG's
display
is set to
none
or its
visibility
is set to
hidden
, the falling divs transition correctly on iOS.


If the SVG
rect
animateTransform
repeatCount
is set to a number (rather than
indefinite
), the falling divs transition correctly in iOS.


If the I remove
transition
from the D3 selection-styling line, the falling div appears at the bottom of the window in iOS rather than sticking to the top.


If I connect my mobile device to my Mac and run Safari Dev Tools, I see that the setting of the
top
property on my
div.single
elements altogether fails to occur when the
svg
is displayed.

top
is added to each
div.single
when
app.parallaxField.style.display = 'none'
:

<code>top</code> is set on each <code>div.single</code>

top
is not added to each
div.single
when
app.parallaxField.style.display = 'inline'
:

<code>top</code> is not set on any <code>div.single</code>

Playing with
z-index
and alternate
display
or
position
options doesn't seem to help.

Any ideas on why one svg's animation would affect D3's transition of the
top
property on separate divs? Thanks!

JS:

var app = {};

var drop = function() {

var numCols = Math.floor(app.windowW / app.singleW) + 1;
var col = Math.floor(Math.random() * numCols);
var positionX = (col * app.singleW - app.singleW / 8) - app.singleW / 2;
var endPositionY = app.windowH - app.singleH;

var single = d3.select('#singles').append('div');
single.attr('class', 'single').style({ left: positionX + 'px' });

// Removing `transition` from the D3 styling makes the blue box appears at bottom of the window in iOS, rather than sticking to the top:
single.transition().style({ top: endPositionY + 'px' });
// single.style({ top: endPositionY + 'px' });

};

document.addEventListener('DOMContentLoaded', function() {

app.parallaxField = document.getElementById('parallax-field');

// Toggling either `display` or `visibility of SVG fixes the falling blue div in iOS:
// app.parallaxField.style.display = 'none';
// app.parallaxField.style.visibility = 'hidden';

app.windowW = window.innerWidth;
app.windowH = window.innerHeight;
app.singleW = 128;
app.singleH = 156;

window.setInterval(drop, 1000);

});


HTML:

<div id="container">

<svg id="parallax-field" width="100%" height="100%">

<defs>
<pattern id="pattern1" patternUnits="userSpaceOnUse" height="200" width="200">
<image x="-40" y="-40" height="200" width="200" xlink:href="path/to/image"></image>
</pattern>
</defs>

<rect id="parallax1" width="100%" height="125%" fill="url(#pattern1)">
<animateTransform
attributeName="transform"
type="translate"
from="0 -100"
to="0 100"
dur="3750ms"
repeatCount="indefinite"
/>
<!-- Changing above `repeatCount` to a number fixes the dropping blue divs after a short delay. -->
</rect>

</svg>

<div id="singles"></div>

</div>


CSS:

#container {
background-color: black;
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
}
.single {
background-color: blue;
height: 156px;
position: absolute;
top: 0;
width: 128px;
}

Answer

Well, I solved it by switching from D3 to jQuery, and by switching from animating the foreground divs' top values to animating their margin-top values instead.

So rather than this (doesn't work with two animating patterned SVGs behind it):

var single = d3
  .select('#singles')
  .append('div')
  .attr('class', 'single')
  .style({ left: positionX + 'px' })
  .transition()
  .duration(app.msPerBeat / 2)
  .style({ top: endPositionY + 'px' });

This code instead does the job:

var single = document.createElement('div');
single.setAttribute('class', 'single');
single.style.left = positionX + 'px';
app.singles.appendChild(single);
$('.single').last().animate({ marginTop: endPositionY + 'px' }, app.msPerBeat / 2);
Comments