Hello I am new to javascript so I apologize if this is silly however, I am creating a generator that displays a .png for each letter of the alphabet. The goal is to overlap and display multiple images inside of a div, with random positions. So far I am able to display images for each letter on top of one another by creating a random z-index, however I can not figure out how to alter the positioning to random for each picture.
I attempted to create a Math.floor random variable called randomThingTwo to alter the top positioning of the images, but this did not work.
Please help!
here is my current code:
var x,y,splitted;
function generate() {
x = document.getElementById("form1");
y = x.elements["message"].value;
var text = [y];
var joined = text.join();
var res = joined.toLowerCase();
var regexp = /[A-z]/g;
splitted = res.match(regexp);
var words = [];
judge();
}
var counter = -1;
function judge() {
if (counter < y.length) {
counter++;
art();
}
}
function art() {
img = new Image(splitted[counter] + '.png');
var picture = document.getElementById("pic");
var img = document.createElement('img');
var randomThing = Math.floor((Math.random() * 10) + 1);
console.log(randomThing);
// var randomThingTwo = Math.floor((Math.random() * 20) + 1);
img.setAttribute("src", splitted[counter]+".png");
img.style.zIndex= randomThing;
img.style.position= "absolute";
// img.style.position.marginTop = randomThingTwo;
img.setAttribute("width", "304");
img.setAttribute("width", "328");
picture = document.getElementById("pic").appendChild(img);
setTimeout(function () {
judge();
}, 100);
}
When setting properties that way, you need to include 'px' afterward, otherwise it doesn't know what units to use with the number you're passing. Try changing the second line to img.style.marginTop = randomThingTwo + 'px';
Related
I have bunch of numbers in an array and also I have the same amount div.Now I need to first value from array to gave first div and so on.
var xList = [265, 152, 364]
var yList = [125, 452, 215]
And every div have the same class name
function creatContent(e) {
var divMark = document.createElement("div");
divMark.classList = `markers mark`;
var img = $('<img class="comment" src="indeksiraj-1.png" alt="myimage" />');
$(divMark).append(img);
}
How to first value gave to first div second to the second div and so on.
And I was thinking about using css like this.
$(".markers").css({ top: yList + "px", left: xList + "px" });
Firstly, it appears you are mixing raw JS with JQuery. It is recommended that you avoid JQuery these days as the raw JS methods are just as simple, but significantly quicker, and they have been usable for the last 4 years - that is to say in all the browsers still supported by their manufacturers. I have therefore changed all JQuery bits from your question with raw JS bits in my answer.
Basic answer
Now, the correct way to do what you're asking is to perform that inside of your function. For example:
function createContent(xPos, yPos) {
var divMark = document.createElement('div');
divMark.classList = 'markers mark';
divMark.style.top = yPos + 'px';
divMark.style.left = xPos + 'px';
var img = document.createElement('img');
img.classList = 'comment';
img.src = 'indeksiraj-1.png';
img.alt = 'myimage';
divMark.appendChild(img);
}
Then you'll need to loop over the arrays to call the function.
for (var i = 0; i < xList.length && i < yList.length; i++) {
createContent(xList[i], yList[i]);
}
Further consideration
As a consideration, you could use a single array for the xList and yList, which would allow you to change your loop to something more readable
var posList = [
{x: 265, y: 125},
{x: 152, y: 452},
{x: 364, y: 215},
];
posList.forEach(({x, y}) => {
createContent(x, y);
});
Without changing function signature
Having now seen more context via your fiddle, I can see the createContent function is called from a button click, and does far more than you have included. I have kept my change to remove the JQuery bit from your snippet, and put in a placeholder for you to put in the rest of the functionality in the createContent function.
function createContent(e) {
var divMark = document.createElement('div');
divMark.classList = 'markers mark';
var img = document.createElement('img');
img.classList = 'comment';
img.src = 'indeksiraj-1.png';
img.alt = 'myimage';
divMark.appendChild(img);
// ...put the rest of the code in the funtion here
return divMark;
}
for (var i = 0; i < xList.length && i < yList.length; i++) {
var mark = createContent();
mark.style.top = yList[i] + 'px';
mark.style.left = xList[i] + 'px';
}
My brain is not working properly today and I can't seem to figure this out.
I have a RGBA image data stored in an Uint8Array() and need to scale the width only.
var w = 160;
var h = 200;
var depth=4;
var pixels = new Uint8Array(w*h*depth);
I need to scale the pixels array to 320x200 and every attempt I did ended up with a garbled image.
Thanks to Yves Daoust I revisited some of my old attempts at solving this by duplicating every chunk of 4 to the destination, and now I got it working. So thank you Yves :) I do not know what I did wrong earlier.
This is the working code that I ended up with. I'm 100% sure it can be done differently and better, but at this point I am satisfied :)
Utils.prototype.scalePixelsInWidth = function(pixels) {
var w = 320;
var h = 200;
var scanlineWidth = w*4;
var scaledPixels = new Uint8Array(w*h*4);
var a = 0;
for(let row=0;row<h;row++) {
var col2 = 0;
for(let col=0;col<w;col++) {
var srcIndex = col2*4 + (row*(w/2)*4);
var destIndex = col*4 + (row * scanlineWidth);
scaledPixels[destIndex+0] = pixels[srcIndex+0];
scaledPixels[destIndex+1] = pixels[srcIndex+1];
scaledPixels[destIndex+2] = pixels[srcIndex+2];
scaledPixels[destIndex+3] = pixels[srcIndex+3];
a++;
if (a > 1) {
a = 0;
col2++;
}
}
}
return scaledPixels;
}
I want to make sure that my instance of fabric.Image is always visible.
It doesn't need to be 100% visible, but a portion of it should always be seen. I've seen a few other questions that are similar and I've based my solution on them, but I haven't found anything that completely solves the problem.
My current solution works when no rotation (angles) have been applied to the image. If the angle is at 0 then this solution is perfect, but once the angle start changing I'm having problems.
this.canvas.on('object:moving', function (e) {
var obj = e.target;
obj.setCoords();
var boundingRect = obj.getBoundingRect();
var max_pad_left_over_width = 50;//obj.currentWidth * .08;
var max_pad_left_over_height = 50;//obj.currentHeight * .08;
var max_top_pos = -(obj.currentHeight - max_pad_left_over_height);
var max_bottom_pos = obj.canvas.height - max_pad_left_over_height;
var max_left_pos = -(obj.currentWidth - max_pad_left_over_width);
var max_right_pos = obj.canvas.width - max_pad_left_over_width;
if(boundingRect.top < max_top_pos) {
obj.setTop(max_top_pos);
}
if(boundingRect.left < max_left_pos){
obj.setLeft(max_left_pos);
}
if(boundingRect.top > max_bottom_pos) {
obj.setTop(max_bottom_pos);
}
if(boundingRect.left > max_right_pos) {
obj.setLeft(max_right_pos);
}
});
I've created an example: https://jsfiddle.net/krio/wg0aL8ef/24/
Notice how when no rotation (angle) is applied you cannot force the image to leave the visible canvas. Now, add some rotation to the image and move it around. How can I get the rotated image to also always stay within view? Thanks.
Fabric.js provides with two object properties isContainedWithinRect and intersectsWithRect which we can use to solve the problem.
An object should not be allowed to go outside the canvas boundaries. This can be achieved if we somehow manage to make sure that the object is either inside the canvas, or at least intersects with the canvas boundaries.
For this case, since you want some portion of the image inside the canvas, we will take a rect smaller than the canvas instead of canvas boundaries to be the containing rect of the image.
Using the properties mentioned to make sure that the above two conditions are satisfied after any movement. Here's how the code looks like:
var canvas = new fabric.Canvas('c');
var imgElement = document.getElementById('my-img');
var imgInstance = new fabric.Image(imgElement, {
top: 0,
left: 0
});
imgInstance.setScaleX(0.6);
imgInstance.setScaleY(0.6);
canvas.add(imgInstance);
var prevLeft = 0,
prevTop = 0;
var max_pad_left_over_width = imgInstance.currentWidth * .08;
var max_pad_left_over_height = imgInstance.currentHeight * .08;
var max_top_pos = max_pad_left_over_height;
var max_bottom_pos = imgInstance.canvas.height - max_pad_left_over_height;
var max_left_pos = max_pad_left_over_width;
var max_right_pos = imgInstance.canvas.width - max_pad_left_over_width;
var pointTL = new fabric.Point(max_left_pos, max_top_pos);
var pointBR = new fabric.Point(max_right_pos, max_bottom_pos);
canvas.on('object:moving', function(e) {
var obj = e.target;
obj.setCoords();
if (!obj.intersectsWithRect(pointTL, pointBR) && !obj.isContainedWithinRect(pointTL, pointBR)) {
obj.setTop(prevTop);
obj.setLeft(prevLeft);
}
prevLeft = obj.left;
prevTop = obj.top;
});
Here's the link to the fiddle: https://jsfiddle.net/1ghvjxbk/ and documentation where these properties are mentioned.
I am building 2D tile game map. Each tile is a div in 2D array (var tiles = []). Below is the function which builds a tile based on some arguments defined somewhere else:
function Tile(rnd, px, py, nid) {
var self = this;
var _types = ["grass","forest","hills","swamp","forest", "mountains"];
var height = 60;
var width = 60;
var tileID = nid // new numeric tile id
var id = "tile_"+px+py+rnd; // old tile ID
var x = px;
var y = py;
var type = _types[rnd];
var img = 'img/maptiles/'+type+'.png';
this.Draw = function() {
var div = $("<div class='tile'></div>");
div.attr('id',tileID).data('type',type).data('x',x).data('y',y);
div.get(0).tile = self;
div.css({top:height*y, left:width*x});
div.css({"background":"url('"+img+"')"});
div.appendTo('#map-content');
};
this.Alert = function() {
alert("Tile type: "+type+". Tile ID: "+tileID+" ");
};
this.Move = function(){ // moves player to available tile, in this case if player stands to a tile before the clicked one
alert("start move! Tile type: "+type+". Tile ID: "+tileID+" ");
if (playerPosition === tileID - 1) {
$("#player").remove();
$("#????")").append('<img id="player" src="Pictures/maptiles/player.png" />');
playerPosition = tileID;
alert("Player position now: " + playerPosition);
}
};
}
As a result I end up with m x n map made of divs each with a unique numeric ID starting with 1. I know that using numeric IDs for elements is(was?) frowned upon, though HTML5 specs do not actually prohibit this anymore.
Now what I want to do is to place a player icon (player.png) depending on player's position. Starting position is 1 and I am using tiles (aka divs) numeric IDs to calculate legal moves (can move only to bordering tiles).
When a tile is clicked this.Move is called. Its purpose is to check if player can move on a clicked tile (just one IF statement for now to test) and must remove player.png and redraw it on the clicked div.
Here I run into a problem since I need to somehow use the tileID (which is equal to Div ID) to tell browser to append the DIV which is belong clicked (as I obviously do not write a function for every div on the field). I think that since I can get the DIV id on click I can use it somehow.
I tried to experiment with this.div.id:eq("+tileID+") but with no luck.
UPD:
Adding click handler as requested. Note this is within var Map, which is responsible for building the map, rendering and handling some user input:
var Map = new function() {
var maxHorz = 20;
var maxVert = 5;
var tiles = [];
this.init = function() {
for(var i=0; i<maxVert; i++) {
tiles[i] = [];
for(var j=0; j<maxHorz; j++) {
tiles[i][j] = new Tile(Math.random()*6|0, j, i, tileID++);
}
}
Render();
Setup();
};
this.GetMap = function() {
return tiles;
};
var Render = function() {
$.each(tiles, function(k,v){
$.each(v, function(k,t){
t.Draw();
});
});
};
var Setup = function(){
$('#map-content').on('click','div.tile', function(e){
//var tile = tiles[$(this).data('y')][$(this).data('x')];
this.tile.Move();
});
}
this.Redraw = function(x,y) {
};
}
Map.init();
The answer is found, finally :)
$('#player').detach().appendTo($('#'+tileID))
I am attempting to make a an image pre loader. There is an array of low res images and an array of high res images. I create a new image object and run through a loop to preload these images. I then use a line of Jquery to set the background to use a low resolution image until the high resolution object has loaded, and when that loads I use another line of Jquery to switch the background to use this high resolution image.
The problem I am having is that the code skips the low resolution background setting and loads the high res image.
Here is the code:
// Low res image array
var lowres = ["image1lr.jpg", "image2lr.jpg", "image3lr.jpg", "image4lr.jpg"];
// Full res image array
var images = ["image1.jpg", "image2.jpg", "image3.jpg", "image4.jpg"];
// Low res image path
var lowresimagepath = "url(images/rotate-background/low_res/";
// Full res image path
var imagepath = "url(images/rotate-background/";
// Counter
var i = 0;
// Image preloading
for(i=0; i<=images.length; i++)
{
// Create object
imageObj = new Image();
imageObj.src = imagepath + images[i] + ')';
}
//Generate random number
var rannum = Math.floor(Math.random() * images.length);
//Set background image to random low res image
$(".bg_home").css({'background-image': lowresimagepath +lowres[rannum]+')'});
//Once image objects are loaded switch to high res image
imageObj.onLoad= function(){
$(".bg_home").css({'background-image': imagepath + images[rannum] + ')'});
}
The Image objects' src is a css property. It should just be the URL
imageObj.src = imagepath + images[i] + ')'; //Generates "url('whatever');"
//It can't be that, must just be the path.
In my experience image.onLoad isn't very reliable. There are different cases where it doesn't work, e.g. IE or cached images.
I'd take a look at https://github.com/desandro/imagesloaded
I've used that library and it works great.
As for your code, it seems to be rather invalid:
// Low res image array
var lowres = ["image1lr.jpg", "image2lr.jpg", "image3lr.jpg", "image4lr.jpg"];
// Full res image array
var images = ["image1.jpg", "image2.jpg", "image3.jpg", "image4.jpg"];
// Low res image path
var lowresimagepath = "images/rotate-background/low_res/";
// Full res image path
var imagepath = "images/rotate-background/";
//Generate random number
var rannum = Math.floor(Math.random() * images.length);
for(var i = 0; i < images.length; i++) {
var image = new Image();
image.src = imagepath + images[i];
if(i == rannum) {
//Set background image to random low res image
$(".bg_home").css({'background-image': 'url(' + lowresimagepath +lowres[i]+')'});
//Once image objects are loaded switch to high res image
iamge.onLoad= function() {
$(".bg_home").css({'background-image': 'url(' + imagepath + images[i] + ')'});
};
}
}
That's working under the assumption that your idea is to have a a single random image added to your background, while all images are loaded in the background.
You're code has lots of problems, here is a clean version I've made, but its not tested.
(function(){ // prevents name conflicting with other scripts
var BASE_PATH_LOW_RES = "images/rotate-background/low_res/",
BASE_PATH_HIGH_RES = "images/rotate-background/"; // since these are constants its better to use capitalized letters
var lastLoadedIndex = 0, // you must ensure all images are loaded and its hard to preload them in parallel
randomIndex = Math.floor(Math.random() * images.length),
images = [
{low: "image1lr.jpg", high: "image1.jpg"},
{low: "image2lr.jpg", high: "image2.jpg"},
{low: "image3lr.jpg", high: "image3.jpg"},
{low: "image4lr.jpg", high: "image4.jpg"}
];
var onComplete = function() {
$(".bg_home").css({'background-image': 'url(' + BASE_PATH_HIGH_RES + images[randomIndex] + ')'});
}
var loadNext = function(){
if(lastLoadedIndex == images.length) { // all images has been preloaded
onComplete();
} else {
var imgTemp = new Image();
imgTemp.src = BASE_PATH_HIGH_RES + images[lastLoadedIndex++].high;
imgTemp.onLoad = loadNext;
}
}
$(".bg_home")
.css({'background-image': 'url(' + BASE_PATH_LOW_RES + images[randomIndex].low + ')'});
loadNext(); // start to preload every image one after another
})();