Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 7 years ago.
Improve this question
I am learning to create a simple animation using a div, javascript and requestAnimationFrame. The animation is working, but with some visual issues which do not seem correct. The issues occur in multiple browsers, although I'm primarily using Chrome. I've created an example with minimal code to demonstrate the issues. I am aware of css animations, webGl, etc exist and may be superior, but want to understand why this code does not work as expected.
One issue is blurring along all edges of a moving div, especially the leading and trailing. The blurring occurs when the div moves quickly. Is this normal and unavoidable? I see the same issue when playing with code written by other people, through that may only mean we are all making the same mistake. I want to render a crisp image, not a blurred image. I'm hoping that as a newbie to animation I'm making a really dumb mistake that is easy to fix.
Another issue are occasional blink effects along the trailing edge of a moving div. They don't occur on every frame. Based on Chrome timeline the frame rates are fine. I have no clue when is causing this.
The example code runs everywhere, but is sized for a desktop.
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=windows-1252">
<style>
.testAnim {
background: red;
height: 100px;
width: 100px;
position: absolute;
}
</style>
</head>
<body width="100%" height="100%" >
<div id="testAnim1" class="testAnim"></div>
<script>
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
var movers = document.querySelectorAll('.testAnim');
for(var m = 0; m < movers.length; m++) {
movers[m].posX = 20;
movers[m].posY = 10;
movers[m].deltaX = 20;
movers[m].deltaY = 0;
};
function update(timestamp) {
for(var m = 0; m < movers.length; m++) {
if (((movers[m].posX + 5 + movers[m].deltaX) > 1200) ||
((movers[m].posX - 5 + movers[m].deltaX) < 0)) {
movers[m].deltaX *= -1;
};
if (((movers[m].posY + 5 + movers[m].deltaY) > 500) ||
((movers[m].posY - 5 + movers[m].deltaY) < 0)) {
movers[m].deltaY *= -1;
};
movers[m].posX += movers[m].deltaX;
movers[m].posY += movers[m].deltaY;
movers[m].style.left = movers[m].posX + 'px';
movers[m].style.top = movers[m].posY + 'px';
//movers[m].style.webkitTransform = "translate3d( "+ movers[m].posX +"px, "+ movers[m].posY +"px, "+ 0 +"px)";
};
window.requestAnimationFrame( update );
};
window.requestAnimationFrame( update );
</script>
</body>
No answer was found. Given the terrible performance of moving html divs this question was more academic than practical.
Related
I'm not sure how to explain what I exactly want (which makes it really hard to google for as well), but I would like to create a table with each cell a specific width and height (let's say 40x40 pixels).
This table will be way larger than the viewport, so it would need some scrolling, but I don't want scrollbars (this part ain't a problem), but a way to drag the table around "behind the viewport".
To make it a bit more complex, I need to be able to center a specific cell and know which cell is centered too (although if I know for example that the table is on left: -520px; I can calculate what the situation is).
Long story short, I'm looking for something that looks and works like maps.google.com, but then with a table and cells instead of a canvas or divs.
What you're trying to achieve is relatively simple. You have a container div element with position: relative; overflow: hidden applied via CSS and the content set to position: absolute. For example:
<div class="wrapper">
<div class="content-grid">
... your grid HTML ...
</div>
</div>
You then need to set up some mouse/touch tracking javascript, you can find plenty of examples around Stack Overflow or Google, which monitors the position of the mouse at mousedown or touchstart events and then tests this repeatedly on an interval to see where the pointer is and update the content-grid top and left position, until mouseup or touchend.
You can make this animation smooth using CSS transition on left and top.
The tricky bit is calculating the position for the centre cell. For this I would recommend calculating a central zone, the size of your grid cells (i.e. 40x40) in the middle of your container element. Then checking if any grid cell is currently more than 1/4 inside that zone, and treating it as the "central" element.
Here is a basic example for position a cell within a grid within a wrapper: https://jsfiddle.net/tawt430e/1/
Hope that helps!
I was a bit disappointed to see the down votes of my question at first. I can imagine that stackoverflow has a lot of issues with new people just trying to get their code written here, but that's not what I asked.
Anyway, with the "you share the problem, you share the solution", mindset, I fixed the code with help of tw_hoff and it all works now. It even saves the coordinates in the local storage so this example HTML keeps you in the same position if you refresh the page. I added the two example images I used as well (store the left one as farm.png, the right one as tile.png, same directory as the html page).
The actual code:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Game map demo</title>
<style>
body { margin: 0; padding: 0; }
</style>
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
</head>
<body>
<div id="map" style="position: absolute; overflow: hidden; width: 100%; height: 100%; background-color: darkgreen;">
<div id="content" style="white-space: nowrap;">
</div>
</div>
<script>
for(i = 0; i < 20; i++) {
for(j = 0; j < 20; j++) {
var tile;
if((i == 4 || i == 5) && (j == 2 || j == 3)) {
tile = 'farm';
} else {
tile = 'tile';
}
$("#content").append('<div style="background: url(\'' + tile + '.png\'); width: 128px; height: 128px; position: absolute; margin-left: ' + (i * 128) + 'px; margin-top: ' + (j * 128) + 'px;"></div>');
}
}
$("body").css("-webkit-user-select","none");
$("body").css("-moz-user-select","none");
$("body").css("-ms-user-select","none");
$("body").css("-o-user-select","none");
$("body").css("user-select","none");
var down = false;
var current_left = 0;
var current_top = 0;
if(localStorage.getItem("current_left") && localStorage.getItem("current_top")) {
current_left = Number(localStorage.getItem("current_left"));
current_top = Number(localStorage.getItem("current_top"));
console.log(current_left);
$("#content").css('marginLeft', (current_left) + 'px');
$("#content").css('marginTop', (current_top) + 'px');
}
$(document).mousedown(function() {
down = true;
}).mouseup(function() {
down = false;
});
var cache_pageX;
var cache_pageY;
$( "#map" ).mousemove(function( event ) {
if(down == true) {
current_left += (-1 * (cache_pageX - event.pageX));
if(current_left > 0)
current_left = 0;
if(current_left < (-2560 + $("#map").width()))
current_left = (-2560 + $("#map").width());
current_top += (-1 * (cache_pageY - event.pageY));
if(current_top > 0)
current_top = 0;
if(current_top < (-2560 + $("#map").height()))
current_top = (-2560 + $("#map").height());
localStorage.setItem("current_left", current_left);
localStorage.setItem("current_top", current_top);
$("#content").css('marginLeft', (current_left) + 'px');
$("#content").css('marginTop', (current_top) + 'px');
}
cache_pageX = event.pageX;
cache_pageY = event.pageY;
});
</script>
</body>
</html>
It still needs some work, and of course a lot of work to be actually used in a game, but this part works and I hope if someone else ever might have the same issue and searches for it on stackoverflow my solutions gives them a push in the right direction :).
Thanks for those that helped!
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 6 years ago.
Improve this question
What I'm Trying To Achieve:
With a wrapper for a display plane and an inner element with any text within;
Calculate center;
Get width of wrapper
Get width of text prior separation
( Wrapper width / 2 ) - Text width = where first letter will go
Break up text into own div elements - I don't require but for anyone looking to use any answers, you may want to replace spaces for
Set position of each letter container to be outside of container to the right
Animate each letter elements margin with an end ease effect;
First to middle position
All following to end position minus total widths of already moved letters.
Hold for a couple of seconds
Each letter element does the same going outside of the plane to the left with a slight delay.
Repeat
In A Less Confusing Nut Shell
Each letter comes on with a slightly delayed starting time to the center of the wrapper, holds there and then goes out of the viewport. I am personally doing this for a loading animation.
My Attempt So Far:
<div class="LoadWrap">
<div class="Loading">Loading</div>
</div>
<script type="text/javascript" src="Assets/JS/jquery-3.1.1.js"></script>
<script type="text/javascript" src="Assets/JS/jquery-ui-1.12.1/jquery-ui.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
var Elem = $('.Loading'),
EWid = Elem.width(),
EStr = Elem.html(),
ESLe = EStr.length,
EOWi = Elem.parent().width(),
ABCD = (EOWi - EWid) / 2,
CTWi = 0;
Elem.html("");
for (var i = 0, len = ESLe; i < len; i++) {
Elem.append("<div style=\"margin-left: " + EOWi + "px;\">" + EStr[i] + "</div>");
}
for (var i = 0, len = ESLe; i < len; i++) {
var ThisWidth = $(".Loading > div:nth-of-type(i)").width();
console.log(ThisWidth);
//setTimeout(
// function() {
// $("#full-wrapper #full").animate({
// marginLeft: '-=938px'
// },{
// easing: 'easing',
// duration: 250,
// });
// }, 500);
}
});
</script>
Problems I'm Experiencing:
':nth-of-type( number )' seems to work however :nth-of-type(i) will not.
You need to concatenate the number
var ThisWidth = $(".Loading > div:nth-of-type(" + i + ")").width();
Here's what StackOverflow looks like on my (huge) work monitor:
That is a lot of white space on either side of the site's actual content.
I get that this is how a very large percentage of websites are designed—so I'm not singling out SO here—but that's actually exactly why I'm asking this question. I'm thinking it'd be really nice if I had some reliable way (say, via JavaScript) of determining the "actual" width of a website, which I could then use to write a quick script that would auto expand any site I'm browsing to fill the available width on my monitor. As it is, I find it absurd that sometimes I still squint before reading tiny text before realizing/remembering to zoom in to take advantage of my enormous screen.
Ahh... much better.
I suspect this is possible, at least to a reasonable degree via some heuristic, as my Android phone appears to do something a lot like this when I double-tap on the screen while browsing the web.
This will do something sorta like that. Though probably misses all kinds of edge cases.
// Assuming jQuery for simplicity
var drillIn = function(node) {
var max = 0;
var windowWidth = $(window).width();
var result = 0;
$(node).children().each(function() {
var $this = $(this);
if ($this.width() > max) {
max = $this.width();
}
});
if (0 < max && max < windowWidth) {
return max;
} else {
$(node).children().each(function() {
var childMax = drillIn(this);
if (childMax > result) {
result = childMax;
}
});
return result;
}
};
drillIn(document.body);
Working Fiddle: http://jsfiddle.net/bdL5b/1/
On SO, I get 960 which is right. Basically it drills into the DOM tree to find the widest node closest to the root which is not 0 or the window width. Because usually, close to the root node there is a container node which holds the site content. Usually.
Not sure you will get a 100% reliable solution though. This is a tricky thing because there are a TON of ways to style websites. I bet crazy stuff like horrible use of absolute positioning could be a serious thorn in your ass.
If you use Firefox, Greasemonkey is awesome. It will run Javascript that you write on any page (I have used it on Stack Overflow's site before).
Just use the browser's built-in "inspect element," to get the id of whatever you want to expand and do this:
document.getElementById("content").style.width = "100%"; // content is just an example
I think the class name of the middle boxes is .container so you could do this:
var boxes = document.getElementsByClassName("container");
for(var i = 0; i < boxes.length; i++)
{
boxes[i].style.width = "100%";
}
As far as a heuristic for doing this arbitrarily, there's probably no good way to do it to all web pages in an unbiased way, without significantly messing up the site's appearance.
That being said, this or something similar might work ok:
var divs = document.getElementsByTagName("div");
for(var i = 0; i < divs.length; i++)
{
divs[i].style.minWidth = "90%";
}
Ha! I've got something close (though I'm also going to try Alex's approach):
The following relies on jQuery and is arguably inefficient (it inspects, I believe, every element in the DOM); but it doesn't take any time on my machine and at least works with SO:
(function($) {
function text($element) {
return $.trim($element.clone().children().remove().end().text());
}
function hasContent($element) {
return $element.is(":visible") && text($element).length > 0;
}
function getExtremeEdges($elements) {
var extremeLeft = null;
var extremeRight = null;
$.each($elements, function(i, el) {
var $element = $(el);
var offset = $element.offset();
if (!extremeLeft || offset.left < extremeLeft) {
extremeLeft = offset.left;
}
if (!extremeRight || (offset.left + $element.width()) > extremeRight) {
extremeRight = offset.left + $element.width();
}
});
return [extremeLeft, extremeRight];
}
var $elementsWithContent = $("*").filter(function(i, el) {
return hasContent($(el));
});
var extremeEdges = getExtremeEdges($elementsWithContent);
var width = extremeEdges[1] - extremeEdges[0];
var desiredWidth = $(document).width() * 0.95;
if (width < desiredWidth) {
$("body").css("zoom", (desiredWidth / width));
}
}(jQuery));
Minified (to use as a bookmarklet):
(function(a){function b(b){return a.trim(b.clone().children().remove().end().text())}function c(a){return a.is(":visible")&&b(a).length>0}function d(b){var c=null;var d=null;a.each(b,function(b,e){var f=a(e);var g=f.offset();if(!c||g.left<c){c=g.left}if(!d||g.left+f.width()>d){d=g.left+f.width()}});return[c,d]}var e=a("*").filter(function(b,d){return c(a(d))});var f=d(e);var g=f[1]-f[0];var h=a(document).width()*.95;if(g<h){a("body").css("zoom",h/g)}})(jQuery);
Time to dogfood this puppy for a while...
I think each website will be too different to have a standard was of auto resizing their content. I belive CSS is the key, by using user defined style sheets. Or something like Stylish. See https://superuser.com/questions/128666/custom-per-site-stylesheet-extension-for-firefox
or https://addons.mozilla.org/en-us/firefox/addon/style-sheet-chooser-ii/
Not much progress but I'm putting what I tried up in case it inspires anyone else:
Works much worse than you would think
Make a bookmarklet that makes all children of body have 100% width. Then, if you click the bookmarklet again, it makes all children of children of body have 100% width. This way, the user can just click until the site becomes more pleasing to them :)
var levels = levels ? levels + 1 : 1;
$('body *:nth-child(' + levels + ')').css({ width: '100%' });
.
First approach to try and figure out where the first meaningful content is
Cool puzzle, I'm employing the awesomeness of jQuery. So I'm approaching it by trying to find the first element which has more non-empty .contents() than .children() because contents also fetches text nodes. Here's what I have so far. It's close, but not quite right because it seems to be searching a bit too deep:
$('body *:visible').filter(function(){
return moreNonEmptyContentThanChildren($(this));
}).first();
function moreNonEmptyContentThanChildren(el) {
var contentCount = 0;
var contents = el.contents();
for (c = 0; c < contents.length; c++) {
elc = contents[c];
if (elc.nodeType != 3 || (elc.nodeType == 3 && $.trim($(elc).text()) != '')) {
contentCount ++;
}
}
return contentCount != el.children().length;
}
I'm currently using SmoothDivScroll to implement a rolling stream of images. However, the left Hotspot is going too fast, while the Right Hotspot does not seem to respond properly.
I've set up a fiddle showcasing the problem: http://jsfiddle.net/gUewB/4/
I'm guessing this has something to do with the way the offset is calculated, but I can't find a good fix to solve the issue. My JavaSkills just aren't that great. :/
I know this thread is kind of old. But I had the exact same issue! Even I updated to the latest version (which is at moment of posting version 1.3)
The solution from Pieter Mathys unfortunatelly didn't work for me.
I've found out that the scrolling problem occures if the width of all images in common is smaller than the scrollableArea-div. So all you have to do is to check if there are enough images to fill the scrollableArea. If not, you don't need the SmoothDivScroll anyway.
I have some example code:
... html is already in in DOM ...
imagesLoaded( '#myScrollable', function( element )
{
var $myScrollable = $(element.elements); // just one #myScrollable
var imgWidths = 0;
var images = $myScrollable.find('img');
for(var loop=0; loop < images.length; loop++)
imgWidths += $(images[loop]).width();
if( imgWidths > $myScrollable.width() )
{
$myScrollable.smoothDivScroll(
{
manualContinuousScrolling: true
});
$myScrollable.smoothDivScroll("recalculateScrollableArea");
}
});
But I have to say, that I used a plugin (imagesLoaded) from https://github.com/desandro/imagesloaded to have an event when all images are loaded. Otherwise all images have a size of "0";
I hope this helps anyone.
The error is happening because of stuff like: margin: 0 auto;
Fixed it with the following:
SET UP EVENTS FOR SCROLLING RIGHT:
var x = e.pageX - (this.offsetLeft + el.data("scrollerOffset").left);
Changed to
var x = el.data("hotSpotWidth") + (e.pageX - el.data("scrollerOffset").left - 960);
SET UP EVENTS FOR SCROLLING LEFT:
var x = ((this.offsetLeft + el.data("scrollerOffset").left + el.data("hotSpotWidth")) - e.pageX);
Changed to
var x = el.data("hotSpotWidth") - (e.pageX - el.data("scrollerOffset").left);
I have a debugger log that I've written in JavaScript for a project I'm working on. The log is basically an <aside> tag in HTML5 that only shows when needed. I wanted to play around with the idea of being able to move the log around the screen, as it may overlap certain things (which is fine for my project). However, I can't seem to figure out how to use HTML5 to properly drag and drop the tag so that it can be placed anywhere on the screen (well, or within a <div> element).
After reading on HTML5's drag and drop support, I have a basic understanding of how it works, but I'm not sure where to start when it comes to allowing the div to be placed anywhere (it's z-index is a high value, so as I said, overlapping is fine).
Any suggestions?
Oh, and I'd like to try and avoid using external libraries for this project, wherever possible. I'm trying to do this in pure JavaScript/HTML5.
Drag and drop doesn't move elements around, if you want the element to move when you drop it then you have to set the new position of the element in the drop event. I've done an example which works in Firefox and Chrome, here are the key points:
function drag_start(event) {
var style = window.getComputedStyle(event.target, null);
event.dataTransfer.setData("text/plain",
(parseInt(style.getPropertyValue("left"),10) - event.clientX) + ',' + (parseInt(style.getPropertyValue("top"),10) - event.clientY));
}
The dragstart event works out the offset of the mouse pointer from the left and top of the element and passes it in the dataTransfer. I'm not worrying about passing the ID because there's only one draggable element on the page - no links or images - if you have any of that stuff on your page then you'll have to do a little more work here.
function drop(event) {
var offset = event.dataTransfer.getData("text/plain").split(',');
var dm = document.getElementById('dragme');
dm.style.left = (event.clientX + parseInt(offset[0],10)) + 'px';
dm.style.top = (event.clientY + parseInt(offset[1],10)) + 'px';
event.preventDefault();
return false;
}
The drop event unpacks the offsets and uses them to position the element relative to the mouse pointer.
The dragover event just needs to preventDefault when anything is dragged over. Again, if there is anything else draggable on the page you might need to do something more complex here:
function drag_over(event) {
event.preventDefault();
return false;
}
So bind it to the document.body along with the drop event to capture everything:
var dm = document.getElementById('dragme');
dm.addEventListener('dragstart',drag_start,false);
document.body.addEventListener('dragover',drag_over,false);
document.body.addEventListener('drop',drop,false);
If you want this to work in IE you'll need to convert the aside to an a element, and, of course, all the event binding code will be different. The drag and drop API doesn't work in Opera, or on any mobile browsers as far as I'm aware. Also, I know you said you don't want to use jQuery, but cross browser event binding and manipulating element positions are the sort of things that jQuery makes much easier.
Thanks for your answer. It works great in Chrome and Firefox. I tweaked it to work in IE.Below is the code.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="generator" content="CoffeeCup HTML Editor (www.coffeecup.com)">
<meta name="dcterms.created" content="Fri, 27 Jun 2014 21:02:23 GMT">
<meta name="description" content="">
<meta name="keywords" content="">
<title></title>
<!--[if IE]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<style>
li
{
position: absolute;
left: 0;
top: 0; /* set these so Chrome doesn't return 'auto' from getComputedStyle */
width: 200px;
background: rgba(255,255,255,0.66);
border: 2px solid rgba(0,0,0,0.5);
border-radius: 4px; padding: 8px;
}
</style>
<script>
function drag_start(event) {
var style = window.getComputedStyle(event.target, null);
var str = (parseInt(style.getPropertyValue("left")) - event.clientX) + ',' + (parseInt(style.getPropertyValue("top")) - event.clientY) + ',' + event.target.id;
event.dataTransfer.setData("Text", str);
}
function drop(event) {
var offset = event.dataTransfer.getData("Text").split(',');
var dm = document.getElementById(offset[2]);
dm.style.left = (event.clientX + parseInt(offset[0], 10)) + 'px';
dm.style.top = (event.clientY + parseInt(offset[1], 10)) + 'px';
event.preventDefault();
return false;
}
function drag_over(event) {
event.preventDefault();
return false;
}
</script>
</head>
<body ondragover="drag_over(event)" ondrop="drop(event)">
<ul>
<li id="txt1" draggable="true" ondragstart="drag_start(event)"> Drag this text </li><br>
<li id="txt2" draggable="true" ondragstart="drag_start(event)"> Drag me</li>
</ul>
<p>I never am really satisfied that I understand anything; because, understand it well as I may, my comprehension can only be an infinitesimal fraction of all I want to understand about the many connections and relations which occur to me, how the matter in question was first thought of or arrived at, etc., etc.</p>
<p>In almost every computation a great variety of arrangements for the succession of the processes is possible, and various considerations must influence the selections amongst them for the purposes of a calculating engine. One essential object is to choose that arrangement which shall tend to reduce to a minimum the time necessary for completing the calculation.</p>
<p>Many persons who are not conversant with mathematical studies imagine that because the business of [Babbage’s Analytical Engine] is to give its results in numerical notation, the nature of its processes must consequently be arithmetical and numerical, rather than algebraical and analytical. This is an error. The engine can arrange and combine its numerical quantities exactly as if they were letters or any other general symbols; and in fact it might bring out its results in algebraical notation, were provisions made accordingly.</p>
<p>The Analytical Engine has no pretensions whatever to originate anything. It can do whatever we know how to order it to perform. It can follow analysis, but it has no power of anticipating any analytical revelations or truths. Its province is to assist us in making available what we are already acquainted with.</p>
</body>
</html>
I adjusted a bit robertc's great answer for cases of multiple items. Here the secondclass name is just for another position.
<aside draggable="true" class="dragme" data-item="0">One</aside>
<aside draggable="true" class="dragme second" data-item="1">Two</aside>
Add the data-item attribute to the setDatafunction.
function drag_start(event) {
var style = window.getComputedStyle(event.target, null);
event.dataTransfer.setData("text/plain", (parseInt(style.getPropertyValue("left"), 10) - event.clientX) + ',' + (parseInt(style.getPropertyValue("top"), 10) - event.clientY) + ',' + event.target.getAttribute('data-item'));
}
Target the element that is dragged.
function drop(event) {
var offset = event.dataTransfer.getData("text/plain").split(',');
var dm = document.getElementsByClassName('dragme');
dm[parseInt(offset[2])].style.left = (event.clientX + parseInt(offset[0], 10)) + 'px';
dm[parseInt(offset[2])].style.top = (event.clientY + parseInt(offset[1], 10)) + 'px';
event.preventDefault();
return false;
}
And loop through all elements with a class dragme.
var dm = document.getElementsByClassName('dragme');
for (var i = 0; i < dm.length; i++) {
dm[i].addEventListener('dragstart', drag_start, false);
document.body.addEventListener('dragover', drag_over, false);
document.body.addEventListener('drop', drop, false);
}
pen