Image slider is choppy - javascript

I have put together a plain javascript/css image slider, started out as just a learning exercise but am now looking to apply it in the real world. The problem is that the animation is choppy on my desktop (which is a v. high spec gaming rig) - and even worse on a mobile (to the degree it's not really an animation anymore)
You can see it in action here:
www.chrishowie.co.uk/sands/
jsfiddle isolates much of the pertinent code - it's not a "this doesn't work" issue, so hopefully the fiddle gives enough to help optimize it.
http://jsfiddle.net/9aozrxy8/5/
In summary: I have a DIV with 4 images in a row, each image is 100% the width of the page. I use javascript to translateX (I have tried translate3d as heard this uses GPU, but didnt make much diff) and I set CSS transitions to ease-in the transform.
I also thought that potentially I am just trying to do too much on this site - but then I look at some other sites doing a heck of a lot more and it's smooth as silk. So I guess I'm missing something.
function slideRight() {
if (sliding) {
return false
};
window.sliding = true;
el = document.getElementById("slider");
cst = getComputedStyle(el);
transformst = cst.transform || cst.webkitTransform || cst.mozTransform;
widthst = cst.width;
widthst = widthst.replace("px", ""); // computed width of slider (7680px)
slidewidth = widthst / 4;
transformst = transformst.replace("matrix(", "");
transformst = transformst.replace(")", "");
transformst = transformst.split(",");
transformst = transformst[4]; // returns current transform in px without unit (px)
if (!transformst) {
transformst = 0;
}
var activebtn = "sldr" + Math.round((Number(transformst) / (-1 * slidewidth)));
document.getElementById(activebtn).classList.remove("sliderbuttonactive");
if (activebtn != "sldr3") {
document.getElementById("slider" + Math.round((2 + Number(transformst) / (-1 * slidewidth)))).style.visibility = "visible";
document.getElementById("slider" + Math.round((2 + Number(transformst) / (-1 * slidewidth)))).style.display = "initial";
document.getElementById("slider").style.transform = "translate3d(" + 25 * ((Number(transformst) / (slidewidth)) - 1) + "%, 0, 0)";
document.getElementById("slider").style.transform = "-webkit-translate3d(" + 25 * ((Number(transformst) / (slidewidth)) - 1) + "%, 0, 0)";
document.getElementById("slider").style.transform = "-moz-translate3d(" + 25 * ((Number(transformst) / (slidewidth)) - 1) + "%, 0, 0)";
document.getElementById("slider").style.transform = "-ms-translate3d(" + 25 * ((Number(transformst) / (slidewidth)) - 1) + "%, 0, 0)";
document.getElementById("leftslidebtn").style.visibility = "visible";
document.getElementById("leftslidebtn").style.display = "block";
}
activebtn = activebtn.replace("sldr", "");
activebtn = "sldr" + (1 + Number(activebtn));
document.getElementById(activebtn).classList.add("sliderbuttonactive");
if (Number(activebtn.replace("sldr", "")) == 3) {
document.getElementById("rightslidebtn").style.visibility = "hidden";
document.getElementById("rightslidebtn").style.display = "none";
}
setTimeout(function () {
window.sliding = false
}, 2000);
}
update: still not resolved but on mobile I have made it usable by reducing the image size for small screens and also not displaying images that are off-screen. Not perfectly smooth but getting there.
Thanks a lot,
C

Like Jeremy mentioned, that the "transition" in your JSFiddle caused the problem, it's also causing it on your website.
In your "Main.css" in line 221. Remove the "transition: top ease 2s;" from class .slide
Everything works fine then on Win8.1/Google Chrome/i7

Related

Problem with the '.style.transform=' in JS

I have this SVG header that, on screen scroll, its parts move differently. For this, I am using a script like the following:
let menu = document.getElementById('main-menu');
let lua = document.getElementById('lua');
window.addEventListener('scroll', function(){
let value = window.scrollY;
menu.style.marginTop = value * 0.45 + 'px'; // working just fine
lua.style.transform = "translateY(" + value * 0.25 + ")"; // don't know how to make this work
OBS: I need to use the transform: translate because this SVG element, for some reason, can't be moved with margin or top/left, only translate.
The menu.style work just fine, but on the lua.style case I'm struggling cause I don't know how to write on the JS a CSS such as transform: translateY that merges a child property (translate) inside another property (transform).
I've tried to write the lua.style.transform = "translateY(" + value * 0.25 + ")"; in many different ways, like:
lua.style.transform = value + "translateY(" * 0.25 + ")";
// or
lua.style.translate = value * 0.45 + 'px';
// or
lua.style.translate = value * 0.45;
// or
lua.style.translate = (0,value * 0.45);
but still can't make it work. How can I write it correctly?
DONE! I used the template literal that Dane Landry explained in the comments. Thanks man.
The code:
lua.style.transform = ` translateY(${value * 0.25}px ) `;
And also thanks kmoser for reminding me translate demans a unit of measurement. It does.

Prevent subpixel rendering with svg

I'm working with SVGs currently and came to a dead end.
The SVG has lines, which should scale together with zooming (so that they stay in balance: 100% width 10px --> 10% width 1px for example)
i scale all stroke-widths with this code:
var svgPath = this._svgContainer.find('svg [class*="style"]');
for (var i = 0; i < svgPath.length; ++i) {
var newStrokeWidth = this._oldStrokeWidth[i] * (1 / (width / imgData.w));
$(svgPath[i]).css(
'stroke-width', newStrokeWidth
);
}
Where width is the new width after zoom and imgData.w is the original unscaled width.
The problem with this is, if i zoom in to far. The stroke with becomes to small and leads to sub-pixel rendering. And supposedly black lines get grey-ish.
My Idea was to clip the value at a certain point to prevent it.
But as far as I know, I have to consider the Device Pixel ratio too, because of different screens (desktop, mobile, 4K)
Would be nice If someone can help me with an idea to fix my problem
We finally found a solution for this, in case anyone has the same problems:
1) Because of the panning of this._$svgElement and the calculation of vpx in a completely different section of the code the element is 'between' pixels. ( 100.88945px for x for example). This causes lines to blur.
I fixed this part with a simple Math.round().
this._hammerCanvas.on('panmove', (event: any) => {
const translate3d = 'translate3d(' + Math.round(this._oldDeltaX + ((vpx === imgData.x) ? 0 : vpx) + event.deltaX) + 'px, ' + Math.round(this._oldDeltaY + ((vpy === imgData.y) ? 0 : vpy) + event.deltaY) + 'px, 0)';
this._$svgElement.css({
transform: translate3d
});
}
2) To fix the problem between the SVG viewport and the line strength, I had to implement a method to calculate the strokewidth equal to 1 'real' pixel regarding the svgs dimension.
the updated code looks like this: (This is the inital code, after the SVG was loaded from the server. Inside the zooming, the old code from above is still the same)
const pixelRatio = devicePixelRatio || 1;
const widthRatio = this._initSVGWidth / svgContainerWidth;
const heightRatio = this._initSVGHeight / svgContainerHeight;
this._svgZoomFactor = Math.max(widthRatio, heightRatio);
const strokeWidth1px = this.computeStrokeWidth1px(widthRatio, heightRatio);
for (let i = 0; i < svgPaths.length; ++i) {
this._initalStrokeWidth[i] = parseFloat($(svgPaths[i]).css('stroke-width'));
const newStrokeWidth = Math.max(strokeWidth1px / pixelRatio, this._svgZoomFactor * this._initalStrokeWidth[i]);
$(svgPaths[i])[0].setAttribute('style', 'stroke-width:' + newStrokeWidth);
this._oldStrokeWidth[i] = newStrokeWidth;
}
and the compute:
protected computeStrokeWidth1px (widthRatio: number, heightRatio: number): number {
const viewBox = this._$svgElement[0].getAttribute('viewBox').split(' ');
const viewBoxWidthRatio = parseFloat(viewBox[2]) / this._$svgElement.width();
const viewBoxHeightRatio = parseFloat(viewBox[3]) / this._$svgElement.height();
return widthRatio > heightRatio ? viewBoxWidthRatio : viewBoxHeightRatio;
}
var newStrokeWidth = this._oldStrokeWidth[i] * (1 / (width / imgData.w));
newStrokeWidth = (newStrokeWidth < 1) ? 1 : newStrokeWidth;
newStrokeWidth will always be 1 or greater

Using OpenLayers ZoomBox in JavaFX WebView

I am using JavaFX WebView included in jdk-8u45 to open a web page which shows a map using OpenLayers 2.13.1. I'm trying to zoom in on the map using a ZoomBox with a BoxHandler. The zooming works like it should, but the problem is how the the rectangle is drawn.
The wanted result is that once I click on the map and start dragging, the rectangle should start drawing as I move the mouse. This works fine in all browsers, except inside my WebView. What happens is that only after I have moved my mouse a few cm in both x- and y-direction (e.g. diagonally), the rectangle starts drawing from this position (not the one where I started dragging). I have looked at the coordinates from the different mouse events, and they all seem to be correct, which is confirmed by the fact that it zooms in on the area I actually dragged over (e.g. not the area that is drawn).
JavaScript console.log stmts output coordinates from the moment I click on the map, but nothing is drawn in the beginning of the drag.
Has anyone had similar problems with WebView? As I said, the code works like a charm in all other browsers I have tried (Safari, Firefox, Chrome, IE). I have looked around on the internet, but I haven't been able to find an answer to my problem.
Code taken from BoxHandler.js:
startBox: function (xy) {
;;;console.log(xy);
;;;console.log("startbox xy=" + xy.x + "," + xy.y);
if(this.zoomBox){
this.removeBox();
}
this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
this.dragHandler.start);
this.zoomBox.className = this.boxDivClassName;
this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
this.map.viewPortDiv.appendChild(this.zoomBox);
OpenLayers.Element.addClass(
this.map.viewPortDiv, "olDrawBox"
);
},
/**
* Method: moveBox
*/
moveBox: function (xy) {
var startX = this.dragHandler.start.x;
var startY = this.dragHandler.start.y;
;;;console.log("dragHandler.start.x=" + this.dragHandler.start.x);
;;;console.log("dragHandler.start.y=" + this.dragHandler.start.y);
var deltaX = Math.abs(startX - xy.x);
var deltaY = Math.abs(startY - xy.y);
this.zoomBox.style.width = Math.max(1, deltaX) + "px";
this.zoomBox.style.height = Math.max(1, deltaY) + "px";
this.zoomBox.style.left = xy.x < startX ? xy.x+"px" : startX+"px";
this.zoomBox.style.top = xy.y < startY ? xy.y+"px" : startY+"px";
console.log("zoombox width=" + this.zoomBox.style.width);
console.log("zoombox height=" + this.zoomBox.style.height);
console.log("zoombox left=" + this.zoomBox.style.left);
console.log("zoombox top=" + this.zoomBox.style.top);
// depending on the box model, modify width and height to take borders
// of the box into account
var box = this.getBoxCharacteristics();
;;;console.log("movebox xOffset=" + box.xOffset);
;;;console.log("movebox yOffset=" + box.yOffset);
if (box.newBoxModel) {
if (xy.x > startX) {
this.zoomBox.style.width =
Math.max(1, deltaX - box.xOffset) + "px";
}
if (xy.y > startY) {
this.zoomBox.style.height =
Math.max(1, deltaY - box.yOffset) + "px";
}
}
},
/**
* Method: endBox
*/
endBox: function(end) {
var result;
console.log(this.map.viewPortDiv.lastElementChild.style);
if (Math.abs(this.dragHandler.start.x - end.x) > 5 ||
Math.abs(this.dragHandler.start.y - end.y) > 5) {
var start = this.dragHandler.start;
var top = Math.min(start.y, end.y);
var bottom = Math.max(start.y, end.y);
var left = Math.min(start.x, end.x);
var right = Math.max(start.x, end.x);
result = new OpenLayers.Bounds(left, bottom, right, top);
} else {
result = this.dragHandler.start.clone(); // i.e. OL.Pixel
}
this.removeBox();
this.callback("done", [result]);
}
Also, I don't know if this is relevant, but when I inspect the HTML div element that holds the map (using Firebug Lite) it looks like the top border of the div is further down than it should be. The map (in correct position on the webpage) is extending beyond the top border. This is different behavior than in the other browsers I mentioned.
Any help would be appreciated.

javascript (making collage from images)

javascript code for making collage through images working in mozilla but chrome
$(document).ready(function() {
var canvaswidth = $('.rc-contentholder').width();
var canvasheight = $('.rc-contentholder').height();
function setleftandtop(noofdivision) {
var elements = document.getElementsByClassName('rc-singlecontentholder');
var noofelements = elements.length;
//place the first row
currentelementid = elements[0].id;
$("#" + currentelementid).css('left', '0px');
$("#" + currentelementid).css('top', '0px');
for (j = 1; j < noofdivision; j++) {
previousleft = parseFloat(elements[j - 1].style.left);
top = 0;
previouswidth = parseFloat(elements[j - 1].style.width);
elements[j].style.left = (previousleft + previouswidth) + "px";
elements[j].style.top = 0;
}
//place the remaining rows
for (i = noofdivision; i < noofelements; i++) {
currentid = elements[i - noofdivision].id;
left = parseFloat(elements[i - noofdivision].style.left);
uppertop = parseFloat(elements[i - noofdivision].style.top);
console.log(uppertop)
height = $("#" + currentid).height();
console.log("id" + "=" + currentid + " " + "height" + "=" + ($("#" + currentid).height()));
elements[i].style.left = left + "px";
elements[i].style.top = (uppertop + height) + "px";
}
}
if (canvaswidth >= 1200) {
noofdivision = 5;
singlecontentholderwidth = canvaswidth / noofdivision;
$('.rc-singlecontentholder').css('width', singlecontentholderwidth);
setleftandtop(noofdivision);
} else if (canvaswidth >= 900) {
noofdivision = 4;
singlecontentholderwidth = canvaswidth / noofdivision;
$('.rc-singlecontentholder').css('width', singlecontentholderwidth);
setleftandtop(noofdivision);
} else if (canvaswidth >= 550) {
noofdivision = 3;
singlecontentholderwidth = canvaswidth / noofdivision;
$('.rc-singlecontentholder').css('width', singlecontentholderwidth);
setleftandtop(noofdivision);
} else if (canvaswidth >= 480) {
noofdivision = 2
singlecontentholderwidth = canvaswidth / noofdivision;
$('.rc-singlecontentholder').css('width', singlecontentholderwidth);
setleftandtop(noofdivision);
} else if (canvaswidth < 480) {
noofdivision = 1;
singlecontentholderwidth = canvaswidth / noofdivision;
$('.rc-singlecontentholder').css('width', singlecontentholderwidth);
setleftandtop(noofdivision);
}
});
in chrome the height of the division rc-singlecontentholder keeps changing dont know what is happening please help me. Help would be appreciated.
As a rule of thumb you should always provide a standalone example/snippet/jsfiddle, so it is easy for others to figure out exactly what your problem is. In your code the markup(HTML) is missing and several of your variables are not defined. However I will try to give you a push in the right direction from the information you have provided.
I deduced that you want to determine the amount of images your canvas can fit from an element with class 'rc-containerwidth'. The first thing I want to address is that if you iterate from
var j = 0
you can just use
elements[j]
but this is just a minor esthetic.
I am assuming that you have classes called 'rc-singlecontentholder' with actual content, that would make the divs have a width and a height. With this in mind the real problem I see with your code is that the first for loop sets the first n=(noofdivision-1) elements with class 'rc-singlecontentholder' to have top=0.
The second for loop starts at element number n=i-noofdivison. Since it starts at
var i = noofdivison;
this effectively starts at n=0, which means you are looping over the same elements as in the first loop. You then calculate the variable upperTop as the elements top position which will be 0.
upperTop = top = 0;
and finally sets the top of this element as top + height
elements[i].style.top = (upperTop + height) + 'px';
Which will cause the different elements to be positioned at the height of their container. This will happen to the first n=(noofdivison-1) elements and the rest will be placed depending on what your original css for these elements are, which are hard to guess from your question. These factors coupled together is likely what is causing the height in chrome to be different from the height of the divs in firefox. Hope this can point you in the right direction to bandage your code.
There are several improvements that can be done to the code apart from this, but this could be a good task to solve if you are in the stages of learning JavaScript.
If learning JavaScript is not of the essence then I suggest trying to reuse some of the libraries that already exists for image collages, which should be more robust and time-saving than creating your own.
You can check out one alternative called H5P Collage or check out how we used JavaScript to make it at github. Disclosure: I am one of the developers on the H5P team.

jQuery vertical bubble marquee HTML elements

I'm searching for a good vertical bubble marquee plugin.
Not simple vertical marquee, I'm looking for a good "flash like" effects plugin, something smooth with element marquee from bottom to top of a div content.
Could be really nice but I think it's only in my dreams this plugin
Well, it's not terribly efficient, but this is a good start I think:
jQuery.fn.verticalMarquee = function(vertSpeed, horiSpeed) {
this.css('float', 'left');
vertSpeed = vertSpeed || 1;
horiSpeed = 1/horiSpeed || 1;
var windowH = this.parent().height(),
thisH = this.height(),
parentW = (this.parent().width() - this.width()) / 2,
rand = Math.random() * 1000,
current = this;
this.css('margin-top', windowH + thisH);
this.parent().css('overflow', 'hidden');
setInterval(function() {
current.css({
marginTop: function(n, v) {
return parseFloat(v) - vertSpeed;
},
marginLeft: function(n, v) {
return (Math.sin(new Date().getTime() / (horiSpeed * 1000) + rand) + 1) * parentW;
}
});
}, 15);
setInterval(function() {
if (parseFloat(current.css('margin-top')) < -thisH) {
current.css('margin-top', windowH + thisH);
}
}, 250);
};
$('.message').verticalMarquee(0.5, 1);
It uses Math.sin to move the element horizontally. The function verticalMarquee accepts two arguments, one for vertical speed and the other for horizontal speed. The function can only be called on jQuery objects that contains only one element - during testing anything more than one element been animated at once caused terrible amount of lagging.
See a simple demo here: http://jsfiddle.net/CcccQ/2/
Do you mean something like The Silky Smooth Marquee plugin?

Categories

Resources