Modify elements position and rotation before appending - javascript

I'm a hopeless javascript newbie and I'm trying to insert dynamically created image elements into DOM while positioning and rotating them randomly.
At the moment I'm basically doing this:
Create an array with all the image file paths
Create a div with dynamically created name and insert into DOM
Generate 20 different instances of the images and insert into DOM to the corresponding div
Rotate each image by a random amount and position each image randomly. For rotation I use the jQuery rotate plugin.
I have a feeling I'm doing all this in a very stupid way since I'm first inserting the elements into DOM and THEN manipulating their position and rotation.
Is there a way to first do everything in virtual memory and finally insert the elements to DOM when all the manipulations have already been done?
Here is my current code (sorry for the noobish quality):
$(document).ready(function(){
var wrapper = $('#wrapper'),
function movieCreator(movieName, generateAmount){
var contents=new Array (
'<img class="film '+movieName+'" src="images/'+movieName+'01.png" alt="'+movieName+'" />',
'<img class="film '+movieName+'" src="images/'+movieName+'02.png" alt="'+movieName+'" />',
'<img class="film '+movieName+'" src="images/'+movieName+'03.png" alt="'+movieName+'" />',
'<img class="film '+movieName+'" src="images/'+movieName+'04.png" alt="'+movieName+'" />',
'<img class="film '+movieName+'" src="images/'+movieName+'05.png" alt="'+movieName+'" />',
'<img class="film '+movieName+'" src="images/'+movieName+'06.png" alt="'+movieName+'" />',
'<img class="film '+movieName+'" src="images/'+movieName+'07.png" alt="'+movieName+'" />'
);
var tmp='';
var dynamicIdName = 'box'+movieName;
var dynamicIdCall = '#box'+movieName;
wrapper.append("<div id='"+dynamicIdName+"' class='moviebox'></div>");
var random
for (i=0; i < generateAmount;){
random = Math.floor(Math.random()*contents.length);
tmp += contents[random];
i++
};
$(dynamicIdCall).append(tmp);
$(".film").each(function(){
randomrot = Math.floor(Math.random()*360);
randomposX = Math.floor(Math.random()*200);
randomposY = Math.floor(Math.random()*200);
$(this).rotate(randomrot);
$(this).css({'top': randomposY -40});
$(this).css({'left': randomposX -40});
});
wrapper.on('click','.film',function(){
imageControl(this);
});
} //end movieCreator
movieCreator('terminator', 20);
movieCreator('rambo', 20);
movieCreator('godfather', 20);
movieCreator('matrix', 20);
movieCreator('kingkong', 20);
}); //dom ready

I'm trying to cover lots of things here...
You have an unexpected ,:
var wrapper = $('#wrapper'),
---^
You can use [] instead of new Array():
var contents = [ ... ]
Careful, i is global, use var. Also, you can move i++ to the for loop:
for (var i = 0; i < generateAmount; i++){
---^--- ---^---
Those are common beginner mistakes. Try to grasp those concepts first and then if you want best performance, append everything at last. You can then refactor everything to this, I commented a few things:
var $wrap = $('#wrapper')
var movieCreator = function (name, ammount) {
// Utility function to keep it DRY
var rand = function (len) {
return ~~(Math.random() * len) // ~~ trick Math.floor
}
// Cache your container in a variable
var $container = $('<div/>', {
id: 'box' + name,
'class': 'moviebox'
})
// Generate iamges with an array
// to keep it DRY
var imgs = []
for (var i = 0; i < 7; i++) {
imgs.push(
'<img ' +
'class="film '+ name +'"'+
'src="images/'+ name +'0'+ i + '.png"'+ // If no with more than 10...
'alt="'+ name +'"'+
'/>'
)
}
var randImgs = []
for (var i = 0; i < ammount; i++)
randImgs.push(imgs[rand(imgs.length)])
// Attach events before inserting in DOM
// that way you don't need delegation with on()
var $imgs = $(randImgs.join(''))
$imgs
.click(function(){
imageControl(this)
})
.each(function(){
var rot = rand(360),
posX = rand(200),
posY = rand(200)
$(this)
.css({
top: posY - 40 + 'px', // use units (px, em)
left: posX - 40 + 'px'
})
.rotate(rot)
})
// Finally insert in DOM. It already has
// all events attached and position changed
$wrap.append($container.append($imgs))
}

Related

Fabricjs - How to detect canvas on mouse move?

In my fabricjs application, I had created dynamic canvases(variable's also dynamic). Here, I need to detect particular canvas while mouse move on canvas.
Sample code,
var i = 0, canvasArray = [];
$(this).find('canvas').each(function() {
i++;
var DynamicCanvas = 'canvas_'+i;
canvasArray[DynamicCanvas] = new fabric.Canvas('canvas_'+i,{
width : '200',
height : '200'
});
});
after this, I have 4 different canvases. Last added canvas has been activated. But i need to add object on any canvas.
So that i have to activate canvas using mouse move event. How can i achieve it.? Please help me on this.
Mullainathan,
Here some quick solution using jQuery:
var canvasStr = '';
var canvasArray = [];
var fabricCanvasArray = [];
var htmlStr = '';
var canvas = null;
//generate canavases
for (var i = 0; i < 4; i++){
canvasArray.push('c' + i);
htmlStr += '<canvas id="c' + i + '" width="200" height="200"></canvas>'
}
//append canvasses to the body
$('body').append(htmlStr);
//to the fabricjs parent div elements assign id's and generate string for jQuery with div id's
for (var i in canvasArray){
fabricCanvasArray[i] = new fabric.Canvas(canvasArray[i], {
isDrawingMode: true
});
$('#' + canvasArray[i]).parent().attr('id', ('div' + canvasArray[i]));
canvasStr += '#div' + canvasArray[i];
if (i < canvasArray.length - 1){
canvasStr += ',';
}
}
//jQuery event for mouse over each div element of the fabric canvas
$(canvasStr).mouseover(function(){
for (var i in fabricCanvasArray){
if (fabricCanvasArray[i].lowerCanvasEl.id == $(this).children(':first').attr('id')){
canvas = fabricCanvasArray[i];
canvas.freeDrawingBrush.width = 10;
var r = 255 - i*50;
var g = i * 50;
var b = 200 - i * 40;
canvas.freeDrawingBrush.color = 'rgb(' + r + ',' + g + ',' + b + ')';
canvas.on('mouse:up', function() {
//do your stuff
// canvas.renderAll();
});
break;
}
}
});
Also, you can run fiddle

webGL story sphere popups

I am trying to adapt the really cool looking WebGL Story Sphere, source code and css here. There's one big problem: once you click on a news article, the text of the article in the popup is always the same. I want to modify the code so that when you click on an article, the right text appears in the popup.
I'm working from a set of article texts that I specify in the code, e.g. var captions = ["good","better","best"]. Though the article titles and images populate correctly in the popup, I can't get the text to do so. Can you help me?? Here's what I've got:
// function code
var passvar = null; // failed attempt to store texts for later
function initialize() {
math = tdl.math;
fast = tdl.fast;
canvas = document.getElementById("canvas");
g_fpsTimer = new tdl.fps.FPSTimer();
hack();
canvas.addEventListener("mousedown", handleMouseDown, false);
canvas.addEventListener("mousemove", handleMouseMove, false);
canvas.addEventListener("mouseup", handleMouseUp, false);
// Create a canvas 2d for making textures with text.
g_canvas2d = document.createElement('canvas');
window.two2w = window.two2h = g_tilesize;
g_canvas2d.width = two2w;
g_canvas2d.height = two2h;
g_ctx2d = g_canvas2d.getContext("2d");
window.gl = wu.create3DContext(canvas);
if (g_debug) {
gl = wd.makeDebugContext(gl, undefined, LogGLCall);
}
//gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, gl.TRUE);
// Here is where I specify article titles, images, captions
// Titles and images populate the popup correctly, captions don't...
var titles = ["a","b","c"];
var captions = ["good","better","best"];
var images = ['imagesphere/assets/1.jpg',
'imagesphere/assets/bp/2.png',
'imagesphere/assets/bp/3.png'
];
var headlines = titles.concat( titles);
var blurbs = captions.concat( captions);
var tmpImages = [];
var tmpHeadlines = [];
var tmpCaptions = [];
// make a bunch of textures.
for (var ii = 0; ii < g_imagesDownGrid; ++ii) {
var textures = [];
for (var jj = 0; jj < g_imagesAcrossGrid; ++jj) {
var imgTexture = new ImgTexture();
textures.push(imgTexture);
if (tmpImages.length == 0) {
tmpImages = images.slice();
}
if (tmpHeadlines.length == 0) {
tmpHeadlines = headlines.slice();
}
if (tmpCaptions.length == 0) {
tmpCaptions = blurbs.slice();
}
var rando = math.randomInt(tmpImages.length);
var img = tmpImages.splice(rando, 1)[0];
var headline = tmpHeadlines.splice(rando, 1)[0];
var caption = tmpCaptions.splice(rando, 1)[0];
passvar = caption;
if (img.indexOf('videoplay.jpg') > -1){
window.vidtexture = imgTexture;
images = images.slice(1); // dont use that thumb again.
headlines = 'WebGL Brings Video To the Party as Well'
}
imgTexture.load(img, /* "[" + jj + "/" + ii + "]" + */ headline);
}
g_textures.push(textures);
}
// And here's where I try to put this in a popup, finally
// But passvar, the stored article text, never refreshes!!!
<div id="story" class="prettybox" style="display:none">
<img class="close" src="imagesphere/assets/close.png">
<div id="storyinner">
<input id = "mytext">
<script>document.getElementById("mytext").value = passvar;</script>
</div>
</div>
And here is my click handler code:
function sphereClick(e){
window.console && console.log('click!', e, e.timeStamp);
var selected = g_textures[sel.y][sel.x];
window.selected = selected;
animateGL('eyeRadius', glProp('eyeRadius'), 4, 500);
var wwidth = $(window).width(),
wheight = $(window).height(),
story = $('#story').width( ~~(wwidth / 7 * 4) ).height( ~~(wheight / 6 * 5) ),
width = story.width(),
height = story.height(),
miniwidth = 30;
story.detach()
.find('#storyinner').find('h3,img,caption').remove().end().end()
.show();
story.css({
left : e.pageX,
top : e.pageY,
marginLeft : - width / 2,
marginTop : - height / 2
}).appendTo($body); // we remove and put back on the DOM to reset it to the correct position.
$('style.anim.story').remove();
$('<style class="anim story">')
.text( '.storyopen #story { left : ' + (wwidth / 3 * 2) + 'px !important; top : ' + wheight / 2 + 'px !important; }' )
.appendTo($body);
$(selected.img).prependTo('#storyinner').parent();
$('<h3>').text(selected.msg.replace(/\(.*/,'')).prependTo('#storyinner');
$body.addClass('storyopen');
} // eo sphereClick()
There's a lot wrong here, but here's a start. It won't solve your problem, but it will help you avoid issues like this.
var passvar = null; is a global variable.
Your loop for (var ii = 0; ... sets that global variable to a new value on every iteration.
Later, you click something and the global variable passvar is never changed.
If you want to use this pattern, you need to set passvar from your click handler so it has the value that was clicked. Since you didn't actually post your click handlers, it's hard to advise more.
But this is also a bad pattern, functions take arguments for a good reason. Since you have to find your clicked item in the click handler anyway, why not pass it directly which does involve a shared global variable at all?
var handleMouseUp = function(event) {
var story = findClickedThing(event);
if (obj) {
showPopup(story.texture, story.caption);
}
}
Which brings me to this:
var titles = ["a","b","c"];
var captions = ["good","better","best"];
var images = ['imagesphere/assets/1.jpg',
'imagesphere/assets/bp/2.png',
'imagesphere/assets/bp/3.png'
];
When you have 3 arrays, all of the same length, each array describing a different property of an object, you are doing it wrong. What you want, is one array of objects instead.
var stories = [
{
title: "a",
caption: "good",
image: "imagesphere/assets/1.jpg"
}, {
title: "b",
caption: "better",
image: "imagesphere/assets/bp/2.jpg"
}, {
title: "c",
caption: "best",
image: "imagesphere/assets/bp/3.jpg"
},
];
console.log(stories[1].caption); // "better"
Now once you find the clicked object, you can just ask it what it's caption is. And you can pass the whole object to the popup maker. And no field is handled differently or passed around in a different manner, because you are not passing around the fields. You are passsing the entire object.

jQuery/javascript - Get Image size before load

I have a list of images that is rendered as thumbnails after upload. The issue that I have is I need the dimensions of the fully uploaded images, so that I can run a resize function if sized incorrectly.
Function that builds the image list
function buildEditList(json, galleryID)
{
//Hide the list
$sortable = $('#sortable');
$sortable.hide();
for(i = 0, j = json.revision_images.length; i < j; i++) {
$sortable.append(
"<li id='image_" + json.revision_images[i].id + "'><a class=\"ui-lightbox\" href=\"../data/gallery/"
+ galleryID
+ "/images/album/"
+ json.revision_images[i].OrgImageName
+ "\"><img id=\""
+ json.revision_images[i].id
+ "\" alt=\"\" src=\"../data/gallery/"
+ galleryID
+ "/images/album/"
+ json.revision_images[i].OrgImageName
+ "\"/></a></li>"
).hide();
}
//Set first and last li to 50% so that it fits the format
$('#sortable li img').first().css('width','50%');
$('#sortable li img').last().css('width', '50%');
//Fade images back in
$sortable.fadeIn("fast",function(){
var img = $("#703504"); // Get my img elem -- hard coded at the moment
var pic_real_width, pic_real_height;
$("<img/>") // Make in memory copy of image to avoid css issues
.attr("src", $(img).attr("src"))
.load(function() {
pic_real_width = this.width; // Note: $(this).width() will not
pic_real_height = this.height; // work for in memory images.
});
alert(pic_real_height);
});
}
I have been trying to use the solution from this stackoverflow post, but have yet to get it to work, or it may just be my code. if you have any ideas on this I could use the help. Currently the alert is coming back undefined.
There's a newer js method called naturalHeight or naturalWidth that will return the information you're looking for. Unfortunately it wont work in older IE versions. Here's a function I created a few months back that may work for you. I added a bit of your code below it for an example:
function getNatural($mainImage) {
var mainImage = $mainImage[0],
d = {};
if (mainImage.naturalWidth === undefined) {
var i = new Image();
i.src = mainImage.src;
d.oWidth = i.width;
d.oHeight = i.height;
} else {
d.oWidth = mainImage.naturalWidth;
d.oHeight = mainImage.naturalHeight;
}
return d;
}
var img = $("#703504");
var naturalDimension = getNatural(img);
alert(naturalDimension.oWidth, naturalDimension.oHeight)​

jQuery-Cycle: how to change the transition effect on each new image

That's pretty much what I am trying to accomplish. To have each image appear with a different transition effect.
Can you please help me fix and figure out what's wrong with my code?
jQuery:
var url = 'http://www.mywebsite.com/';
var img_dir = '_img/slideshow/';
var fx =
[
'blindX',
'blindY',
'blindZ',
'cover',
'curtainX',
'curtainY',
'fade',
'fadeZoom',
'growX',
'growY',
'scrollUp',
'scrollDown',
'scrollLeft',
'scrollRight',
'scrollHorz',
'scrollVert',
'shuffle',
'slideX',
'slideY',
'toss',
'turnUp',
'turnDown',
'turnLeft',
'turnRight',
'uncover',
'wipe',
'zoom'
];
var index = 0;
$(function() {
var $slideshow = $('#slideshow');
// add slides to slideshow (images 2-3)
for (var i = 2; i < 13; i++)
$slideshow.append(
'<a href="' + url + img_dir + i+'.jpg" title="Slideshow">'+
//src="http://www.mywebsite.com/_img/slideshow/"
'<img src="' + url + img_dir + i+'.jpg" width="100" height="100" />'+
'</a>');
// start the slideshow
$slideshow.cycle(
{
if( index < fx.length ) { index++; }
else { index = 0; }
fx: fx[index],
timeout: 3000
});
});
To use all the effects it is quite easy:
http://jsfiddle.net/lucuma/tcRCj/14/
$('#slideshow').cycle({fx:'all'});​
Furthermore if you just want to specify certain ones:
$('#slideshow').cycle({fx:'scrollDown,scrollLeft,fade'});
And if you do NOT want to randomize the effect order (it will randomize by default):
$('#slideshow').cycle({fx:'scrollDown,scrollLeft,fade', randomizeEffects:0});

getting JQuery .height() after appending to another div returns 0

I am trying to create a div that is scrollable only when it's above a certain height after appending text to it. I check the height using jquery and it returns zero every time. Any suggestions?
HelpOverlay.prototype.buildContent = function(helpMappings){
this.content = $('<div class="content"></div>');
var table = $('<table></table>');
table.append($('<tr><td class="key"><h3>Key</h3></td><td class="command"><h3>Command</h3></td></tr>'));
this.content.append(table);
for (var whichCategory = 0; whichCategory < helpMappings.categories.length; whichCategory++) {
var category = helpMappings.categories[whichCategory];
var categoryDiv = $('<div class="category">' + category.category + '</div>');
this.content.append(categoryDiv);
var categoryTable = $('<table></table>');
for (var whichMapping = 0; whichMapping < category.mappings.length; whichMapping++) {
var mappings = category.mappings[whichMapping];
var row = $('<tr></tr>');
var keyCell = $('<td class="key">' + mappings.key + '</td>');
var commandCell = $('<td class="command">' + mappings.command + '</td>');
row.append(keyCell);
row.append(commandCell);
categoryTable.append(row);
}
this.content.append(categoryTable);
}
this.helpOverlay.append(this.content);
console.log(this.content.height());
}
console.log(this.content.height()) is returning zero.
In reference to this line:
this.helpOverlay.append(this.content);
Is this.helpOverlay already part of the DOM? Any element that is not inserted into the DOM will return 0 for height and width until it is inserted.
Edit:
this.content.css( 'display', 'none' ).appendTo( 'body' );
var dims = { 'height': this.content.height( ),
'width': this.content.width( ) };
this.content.detach( );
As a side note, you don't need to wrap element creation in $() if you're passing it to another jQuery method. This is your code from above...
table.append($('<tr><td class="key"><h3>Key</h3></td><td class="command"><h3>Command</h3></td></tr>'));
But you could write it (and it would be more efficient execution) this way and it would still work exactly the same, except with 1 less call to $()...
table.append('<tr><td class="key"><h3>Key</h3></td><td class="command"><h3>Command</h3></td></tr>);

Categories

Resources