I was thinking about how to make some cool image effects in browser, and I know it may be a little late to be heading down this train of thought with HTML5/CSS3 up and coming, but I was wondering what the inherent limitations / problem points there would be with implementing a library that essentially created divs each to hold a pixel of an image using background offsets. It is clear that this will create many divs, but if you wanted to work with only rows or columns on a small image it doesn't seem like this would be that unreasonable. With browser caching images, a request wouldn't have to be made for every segment, and the only other potential problem I can see is the processing of the positioning, which I imagine won't be a problem. I don't really have anything at this point to stop from going forward playing with images like this (so I will!), but I'm curious if there is anything that I am overlooking here that would make the idea unfeasible, and especially anything tricky I should be aware of. Thanks :)
Edit: Tried this, and it seems like there is either an inherent problem or a problem in my code (sorry it sucks, was just playing around), use with any image and you will see the difference.
var lpath = "images/logo.png"
window.onload = function(){
console.log('test');
$('body').append("<img id='logo' style='display:none' src="+lpath+">");
console.log($('#logo').width());
console.log('hello');
var logod = $('<div></div>')
.addClass('i')
.width($('#logo').width())
.height($('#logo').height())
.css('background-image','url('+lpath+')')
$('body').append(logod);
for(var i = 1; i <= $('#logo').height(); i++){
var cons = $("<div></div>")
.height(1)
.width($('#logo').width())
.css('background','url('+$('#logo').attr('src')+') no-repeat 0 ' + (-i));
$('body').append(cons);
}
}
Image on the top is just an , image on the bottom is a series of 1px tall divs.
PS Has to do with browser zoom.
It could be very slow. If you are clever you can split only as much as necessary, so there are fewer divs for the browser to deal with. I'm sure you could do it though and it might be fun.
Related
I'm creating a short game in Html5. I'm trying to figure out the best way to do the Hero selection.
Basically there are 113 heroes. I created a spritesheet that is 1320x1320 with each hero img being 120x120. The first picture is actually just a box that says 'Click to pick hero' in it.
My first question is, since it loads my spritesheet at the beginning to load the first image, later on when it loads the rest of the heroes it won't have to reload the image right? Because
setting 'heroPics[i].style.backgroundImage = "url(Heroes.jpg)";' each time makes me feel uneasy.
Second and important question to me. Back when I worked on games for mobiles, I found out that if you loaded an image that's 570 it'd use resources for a 1024x1024 and that it'd be better to remake the image to 512 and just scale it up, saving loads of resources. Is it the same here? My image being 1320 would it use resources as a 2048? Or since I'm loading images 120x120 it's only using resources for 128?
Now on to the real question. When the person clicks on 'Click to pick hero', I want all the hero images to appear. When they pick a hero I'd like to garbage all the variables and the div I just created, because they will not be picking a new hero too often, so it's better to garbage it, right? Or since the spritesheet is already loaded it's worth it to just hide the div containing the images instead? It'd still have all those variables loaded tho? Anyway that's one of my major question.
Second one is, how do I create a scrollbar inside a div dynamically? I believe I could do it if I set all the properties manually but I want to create tags and a search for the heroes, so the scrollbar has to adjust to whatever is currently being searched active, any advice on this one is greatly appreciated.
And last of all, is there a way to create the image at half it's size from the beginning? I tried .style.width = "50%" and height auto but it doesn't work since it's a spritesheet =(. So I use the webkit to scale down the div but I'd prefer another option if possible.
Thanks for reading this far and sorry for all the questions, here is what I've done so far:
function selectHero() {
var gg = 1;
var bg = 0;
for (var i = 1; i < 114; i++) {
heroPics[i] = new Image();
heroPics[i].style.backgroundImage = "url(newHeroes.jpg)";
heroPics[i].style.width = "120px";
heroPics[i].style.height = "120px";
heroPics[i].style.backgroundPosition = (-(120 * i)) + "px" + " " + (-((Math.floor(i / 11)) * 120)) + "px";
heroPics[i].style.position = "absolute";
heroPics[i].style.left = -90 + (75 * gg) + "px";
heroPics[i].style.top = -30 + (75 * bg) + "px";
heroPics[i].style.webkitTransform = 'scale(0.6, 0.6)';
heroPics[i].draggable = false;
someDiv.appendChild(heroPics[i]);
//heroPics[i].addEventListener( "click", heroChosen, false );
gg ++;
if(gg > 17) {
gg = 1;
bg ++;
}
}
}
I heard math.floor uses way too much resources, should I find a different solution even if it's uglier since right now it's calling math.floor 113 times? Thanks once again
Edit:
Found a solution to my last question about resizing images:
background-size = 792px 792px;
Just scaled 1320x1320 down by 60% in the css class and then changed the imgae size from 120 to 72 and it worked.
Also thanks for the useful tip of creating a class that holds the majority of the properties and using JS only when needed. Still need help with the scrollbar and a few others!
Basically there are 113 heroes. I created a spritesheet that is
1320x1320 with each hero img being 120x120. The first picture is
actually just a box that says 'Click to pick hero' in it. My first
question is, since it loads my spritesheet at the beginning to load
the first image, later on when it loads the rest of the heroes it
won't have to reload the image right? Because setting
'heroPics[i].style.backgroundImage = "url(Heroes.jpg)";' each time
makes me feel uneasy.
Yes, but you would probably be better off doing this via CSS.
Second and important question to me. Back when I worked on games for
mobiles, I found out that if you loaded an image that's 570 it'd use
resources for a 1024x1024 and that it'd be better to remake the image
to 512 and just scale it up, saving loads of resources. Is it the same
here? My image being 1320 would it use resources as a 2048? Or since
I'm loading images 120x120 it's only using resources for 128?
First I have heard of that, and it is likely to be browser dependent even if true. On second thought, I did hear that iOS had some issues with loading images that were beyond a certain size, but I'm not certain. The largest image I think I currently use is 1440x570 or so. I'd have to check the sprites, but most of them are much smaller.
Now on to the real question. When the person clicks on 'Click to pick
hero', I want all the hero images to appear. When they pick a hero I'd
like to garbage all the variables and the div I just created, because
they will not be picking a new hero too often, so it's better to
garbage it, right? Or since the spritesheet is already loaded it's
worth it to just hide the div containing the images instead? It'd
still have all those variables loaded tho? Anyway that's one of my
major question.
If you are doing filtering etc, you might try something like using classes on the children of your div. So you would have code like:
<div id="heroselection">
<div class="hero1 fighter male"></div>
<div class="hero2 wizard female"></div>
</div>
Then as you select filters, you can easily go through and hide the ones you don't need. First, hide them all. Then show the ones that match your filters, so if they checkbox "female" then your javascript (I'm using jQuery here, but feel free to pick another):
$('#heroselection > div').hide();
$('#hereselection > div.female').show();
Second one is, how do I create a scrollbar inside a div dynamically? I
believe I could do it if I set all the properties manually but I want
to create tags and a search for the heroes, so the scrollbar has to
adjust to whatever is currently being searched active, any advice on
this one is greatly appreciated.
Sounds like you want overflow:auto or perhaps overflow-y: auto on the div.
And last of all, is there a way to create the image at half it's size
from the beginning? I tried .style.width = "50%" and height auto but
it doesn't work since it's a spritesheet =(. So I use the webkit to
scale down the div but I'd prefer another option if possible.
Sounds like you are looking for background-size
you are creating too much properties using javascript better solution is to create one parent class with common properties and apply this class to all divs and modify remaining properties with Javascript.
#parent > div{
background:url('newHeroes.jpg');
width:120px;
height:120px;
}
If you are familiar with SASS style of writing CSS then you can write sass and compile to css for all child div elements
#for $i from 1 through 114 {
div:nth-child(#{$i}) {
/* example --width: 100% / #{$i}*/
}
}
Hello StackOverflow Community,
what I am trying to achieve is a header that can be moved with the mouse.
You klick into the header and drag the mouse and the elements inside the header will move with different speeds.
I achieved the parallaxing part but the performance is not really good. It is partially a bit laggy while dragging the backgrounds.
My question now is: what can be changed in the code to get a performance boost?
That's the part of the code that takes care of parallaxing. On every mousemove a each loop is executed which I think is the reason for the performance beeing so laggy:
var dragging = false;
var clickMouseX;
//Our object for the layers
//each layer has a different scrolling speed
var movingObjects = {
'#header-l1' : {'speed': 1},
'#header-l2' : {'speed': 1.4},
'#header-l3' : {'speed': 1.85},
'#header-l4' : {'speed': 2.2},
};
$('#header-wrapper').mousedown(function(e){
dragging = true;
//Get initial mouse position when clicked
clickMouseX = e.pageX;
$(this).mousemove(function(mme){
//execute only if mousedown
if(dragging){
//iterate through all layers which have to be parallaxed
$.each(movingObjects, function(el, opt){
var element = $(el);
//get difference of initial mouse position and current mouse position
var diff = clickMouseX - mme.pageX;
//scroll-position left speed 1
if(diff < 0) diff = -1;
//scroll position right speed 1
if(diff >= 0) diff = 1;
//get current position of layer
currLeft = parseInt(element.css('left'));
//get current layer width
elWidth = element.width();
//if right border is reached don't scroll further
if(currLeft < -(elWidth - 810)){
element.css('left', -(elWidth - 810));
}
//so do with left border
if(currLeft > 0){
element.css('left', 0);
}
//parallax it! Subtract the scroll position speed multiplied by the speed of the desired
//layer from the current left property
element.css('left', parseInt(element.css('left')) - diff*opt.speed);
});
}
});
/* Cursor */
$(this).css('cursor', 'pointer');
return false;
});
I also put a fiddle up:
http://jsfiddle.net/yWGDz/
Thanks in advance,
Thomas
P.S. maybe someone even finds out why layer two and three have the same scroll speed while having different speeds defined.
I worked at this a bit, and came up with this: http://jsfiddle.net/amqER/2/
This works a lot faster than the original (especially in firefox, where it performs a whole lot better, chrome it's still pretty slow). I also changed up some of the logic in your code, to make it make more sense.
A list of things that I did:
Minify your pngs
2 of your png files were over 2 megs, so I threw them into a png compressor (tinypng) and that reduced the size a lot. This helps with loading time and overall snappiness.
Re-use values as much as possible
In your original code, you wrote to and then subsequently read from the css left property a couple times in your code. Doing this is going to make it a lot slower. Instead, I kept an left property, and would only touch $.css when I absolutely needed to. Likewise for reading each element's width each update.
Also, like I said, I modified your logic to (I think) make more sense, given what you were trying to accomplish. It calculates a new diff each update, and tries to move according to that. Also, it doesn't try to keep moving once one of the images falls off (which yours does if you move all the way to the right, and it looks really weird). You can also look at this: http://jsfiddle.net/amqER/5/, which maybe is more like the control scheme you wanted.
Just some quick performance tips.
Try not to use $(this).mousemove instead save $(this) into a variable and use that.
var th = $(this);
th.mousemove...
Try to avoid using $.each. This is probably the part that's slowing your code down.
You can replace it with a for loop, but I would suggest, in this case, sending in each element one by one.
var parallax = function(img){
};
parallax(img1);
parallax(img2);
instantly-increase-your-jquery-performance
Whilst Xymostech's answer does greatly improve upon the original poster's original code; the performance is hardly improved for me in Chrome.
Whilst inspecting the page FPS, the solution posted here runs at 15FPS for me on a Retina MacBook Pro.
I made a very simple change to the code, altering it to use translate3d properties instead of left. Now, it runs at 55-60 FPS for me. I'd call that a massive performance boost.
If 'show paint rectangles' are turned on in Chrome, you'll see the previously posted solution is continually painting changes to the dom whilst the parallax is in motion. With the translate3d solution, there's simply zero painting done the whole time the parallax is in motion.
http://jsfiddle.net/LG47e/
I am trying to create/invent a new javascript slider object which will work by displaying a base line image:
http://imgur.com/DuVkE.png
then I want to use these 'knobs' to layer on top depending on certain circumstances
http://imgur.com/GKkqx.png
These have already been 'cut up' and will be placed on one of the three black knobs. I have many different colors because I plan to run through them so that the color appears to transform from one, to the other.
So I need to be able to attach an image to the id I received from the user and then manipulate the image later.
My code:
<div id='option1'></div>
<script type="text/javascript">
var slide1 = new slider("option1");
My constructor will look something like this:
function slider(id) {
var obj = document.getElementById(id);
if (!obj) {
var state = -1;
return -1;
}
var state = 0; //blank state
//alert("in");
//alert(document.getElementById(id).className);
//this.addClass("hSliderBack"); INCORRECT SYNTAX!!!
$("#"+id).addClass("hSliderBack"); //this works
}
I fixed the problem with the addClass above, though a little ugly.
My CSS script:
.hSliderBack
{
background-image: url('/Switches/switchLine.png');
background-repeat: no-repeat;
padding-left: 2px; /* width of the image plus a little extra padding */
display: block; /* may not need this, but I've found I do */
}
This is how I can add a picture to my constructor. Still a lot of work to do, but at least it's a start. Any comments are still appreciated as I am very green!!
What you write here:
//obj.innerHTML = "<img src=' this doesn't seem right to me.
is in fact one perfectly reasonable and viable way. You enter into the DOM the <img> node referencing the image you want to display.
However, more common and perhaps more maintainable solution in many cases is to have a CSS style that references a background image, and you enter a <div> into the DOM using the style that causes your image to be displayed.
You should ask yourself, though, is it best to do this without any support from tools. Many of the most popular JavaScript libraries have tools like this built in, or at the very least, have methods that make building this type of code much, much easier.
Of course, if you are doing this to learn the basics of web development before using a framework so you understand what they are doing more thoroughly, more power to you :-)
I have this crazy bug that only comes up sometimes. It was apparent when I was developing this site but then it disappeared for a week or so and now that the site is live it's back. I don't think it has anything to do with my hosting because it bugs out locally as well.
My problem is that I'm swapping the css value background-image on each click. It works perfectly 95% of the time, but sometimes for a span of like 15 minutes it just won't display about half the images, seemingly randomly. The strangest thing is that if you look in the inspector you can see that the script correctly changed the css value, but the image simply wasn't loaded. I have no idea why!
Here's the website: shouldivoteoliver.com It's on the "Propaganda" page.
Here's the Javascript:
$(document).ready(function() {
var n=0;
$(".button").click(function(){
if (n===5){
$('<video style="position:relative;left:250px;" src="http://dl.dropbox.com/u/1011105/6.ogg" controls="controls">your browser does not support the video tag</video>').appendTo($('#putin'));
n++;
$("#putin").css("background-image","none");
}
else{
$('video').remove();
$("#putin").css("background-image",function(){
if (n>13){
n=1;
return ('url(images/1.jpg)');
}
else{
n++;
return ('url(images/'+n+'.jpg)');
}
});
}
});
});
I would suggest using a background image as a sprite and changing the background position as
opposed to changing the background image property.
Here is a tutorial that I googled
http://www.noobcube.com/tutorials/html-css/css-background-image-sprites-a-beginners-guide-/
I tried going through all the slides on the propaganda twice but I didn't come across any problem. I don't know what is going on, but you can make your code a little cleaner and more readable by just making an array to store the contents of each "slide", and simply loop through it on mouse click. I guess you don't really need to set the background property of that div and you could just include an image.
Just an advice, not sure if it will help with your problem, but will make this thing more manageable and easier to add more stuff.
Here's a one-liner
swap value at index i1 with i2
arr.slice(0,i1).concat(ar[i2],ar.slice(i1+1,i2),ar[i1],.slice(i2+1))
I have a webpage where I want the user to see a new image when they put thier mouse over a certain part of the image. I used an image map.
<img src="pic.jpg" usemap="#picmap" />
<map id="picmap" name="picmap"><area shape="rect" coords ="10,20,30,40"
onMouseOver="mouse_on_write('mouse is on spot')"
onMouseOut="mouse_off('mouse is off spot')"
href="http://www....html" target="_blank" />
</map>
<p id="desc"></p>
Where in the header I defined these functions:
<script type="text/javascript">
function mouse_off(txt)
{
document.getElementById("desc").innerHTML=txt;
document.p1.src="pic.jpg";
}
function mouse_on_write(txt)
{
document.getElementById("desc").innerHTML=txt;
document.p1.src="pic2.jpg";
</script>
It works, but it is slow. When the mouse is put over the second image it takes some few seconds to appear; my temporary solution was to drastically reduce the size of the images because they were huge (at 2.5mb they switch fast now, but still not seamless). How can I make the image switching more seamless without reduction in picture quality?
On second thought I realize that I could also just have both images displayed, at a small and a large scale, and on mouse over they would switch places; How would I do this? Would this reduce lag?
You don't need to create any page elements, it can all be preloaded using JavaScript:
tempImg = new Image()
tempImg.src="pic2.jpg"
EDIT:
If you have a lot of images, you can use the poor-man's multi-preloader:
preloads = "red.gif,green.gif,blue.gif".split(",")
var tempImg = []
for(var x=0;x<preloads.length;x++) {
tempImg[x] = new Image()
tempImg[x].src = preloads[x]
}
Doing this with sprites is a good solution, because you don't have to wait to load the new image. Sprites work by combining the two images into one, and changing the background offset on mouseover.
You can even do with with CSS instead, for much faster results. There's a good tutorial on this here.
As of Javascript 1.6 this can be accomplished without any named variables:
imageList.forEach( function(path) { new Image().src=path } );
You can also put both images in same file and offset it up and down. If it should affect element you are crossing over with mouse it could look like
a {
background-image: url(back.png);
background-repeat: no-repeat;
background-attachment:fixed;
background-position: 0 0;
}
a:hover {
background-image: url(back.png);
background-repeat: no-repeat;
background-attachment:fixed;
background-position: 0 20px;
}
This way it can work without javascript.
If I understand your case correctly you still need javascript, but you can "preload" image this way nevertheless.
What you want todo is preload the images behind the scenes.
Then, when moused over, the browser will already have that image in its cache and will switch it over very fast.
function preloadImage(imagePath)
{
var img = document.createElement('IMG');
img.src = imagePath;
}
preloadImage('BigImage');
Clever solution from Diodeus. However, unless there's a good reason NOT TO, you should really consider using sprites. It's a bit of work to get them setup, but the net efficiency is really worth it.
This approach is the number one rule in Steve Souder's High Performance Web Sites.
"Rule 1 - Make Fewer HTTP Requests"
Good luck and have fun. - D.
I've noticed that 'preloading' into .src to this day doesn't work consistently across all browsers - IE7 still can't figure out how to cache / use preloaded images - you can clearly see there's a server request made every time you mouse over.
What I do is load in all images via standard HTML placement and just toggle style.display on and off.
Use display: none;, then have the Javascript change it to display: inline when you want to display it. This has the added advantage of being able to put the image exactly where you want in the page's source, rather than having to add it with Javascript later.
Here's how I do it, in pure JavaScript:
var myImgs = ['path/to/img1.jpg', 'path/to/img2.gif'];
function preload(imgs) {
var img;
for (var i = 0, len = imgs.length; i < len; ++i) {
img = new Image();
img.src = imgs[i];
}
}
preload(myImgs);
That said, ALassek's suggestion of using CSS sprites is an excellent one, if you have scope to do it. The advantages of sprites are many: fewer HTTP requests, smaller download size (usually), works without JavaScript enabled.
http://www.filamentgroup.com/lab/update_automatically_preload_images_from_css_with_jquery/
When we first launched the lab, we released a jQuery plugin that automatically preloads all images referenced in CSS files. We've found the script to be incredibly helpful in developing snappy applications where images are always ready when we need them. This post describes a significant update to the script which will make it even easier to integrate in existing projects.