I have a class that I've created, called Sprite:
var Sprite = function Sprite()
{
that = this;
that.xPos = 0;
that.yPos = 0;
…
that.image = null;
this.render =function()
{
…
}
this.setImage(filename)
{
that.image = new Image();
that.image.src = filename;
}
}
And then I create an array of objects:
var names=[
{filename:"a1.png"},
{filename:"a2.png"},
{filename:"a3.png"},
{filename:"a4.png"}
];
var objs = [];
for(var l=0;l<names.length;l++)
{
objs.push({});
objs[l] = new Sprite();
…
setImage(names[l]);
}
Every object in my array point to the same image (with the same image file.)
Can anybody tell me what I'm doing wrong here?
Is there a better way I can do this?
Thanks.
your setImage(names[l]); in the loop seems to be overwriting hence you get same image, try doing something like:
var Sprite = function Sprite() {
that = this;
that.xPos = 0;
that.yPos = 0;
that.image = null;
this.setImage = function(filename) {
that.image = new Image();
that.image.src = filename;
};
}
var names=[
{'filename':"a1.png"},
{'filename':"a2.png"},
{'filename':"a3.png"},
{'filename':"a4.png"}
];
var objs = [];
for(var l=0;l<names.length;l++) {
var sp = new Sprite();
//set image for the new sprite object
sp.setImage(names[l].filename);
objs.push(sp); //push sp object to objs array
}
console.log( objs );
Related
If I have an array:
var imgArray = New Array();
imgArray[0] = "example_A"
imgArray[1] = "example_B"
imgArray[2] = "example_C"
imgArray[3] = "example_D"
...
imgArray[n] = "example_n"
and my javascript sheet is:
var imgArray = New Array();
imgArray[0] = "example_A"
imgArray[1] = "example_B"
imgArray[2] = "example_C"
imgArray[3] = "example_D"
...
imgArray[n] = "example_n"
var linkArray = New Array();
linkArray[0] = "example_A"
linkArray[1] = "example_B"
linkArray[2] = "example_C"
linkArray[3] = "example_D"
...
linkArray[n] = "example_n"
function diceCast(){return Math.floor(Math.random()*n+1);};
function showImage(){
var imgNum = diceCast();
var objImg = document.getElementById("mainImg");
objImg.src = imgArray[imgNum];
objImg.onclick = ()=>window.open(linkArray[imgNum], '_blank');
}
How can I make array number '[n]' automatically?
you have to change diceCast to include the imgArray.length instead of using n
var imgArray = [
'https://dummyimage.com/100x100/000/f00',
'https://dummyimage.com/100x100/f00/000'
]
var linkArray = [
'https://example.com',
'https://google.com'
]
const diceCast = () => Math.floor(Math.random() * imgArray.length)
function showImage(){
var imgIndex = diceCast()
var objImg = document.getElementById("mainImg")
objImg.src = imgArray[imgIndex]
objImg.onclick = () => {
console.log('opening', linkArray[imgIndex])
window.open(linkArray[imgIndex], '_blank')
}
}
showImage()
<img id=mainImg>
I believe that you can achieve the desired effect through the following code snippet:
function getArray(n){
var arr = new Array();
for (var i = 0; i < n; i++) {
arr.push("example_" + String.fromCharCode(65 + i)); // Start from 'A'
}
return arr;
}
console.log("Array: %o", getArray(10));
EDIT:
In case n exceeds the alphabet/printable chars you may prepend as HEX e.g.
arr.push("example_" + (10 + i).toString(16));
I want to loop over files selected for upload, get the file signature and return a array of file signatures. The listOfFileSignatures array is empty outside the readFirstFourBytes function. Is their a way to make it accessible globally?
var listOfFileSignatures = [];
var totalSize;
var uploadedFiles = document.getElementById("notes").files;
for (file of uploadedFiles) {
var blob = file;
var fileReader = new FileReader();
fileReader.onloadend = function readFirstFourBytes(e) {
var arr = (new Uint8Array(e.target.result)).subarray(0, 4);
var fileSignature = "";
for (var i = 0; i < arr.length; i++) {
fileSignature += arr[i].toString(16);
};
listOfFileSignatures.push(fileSignature);
console.log(listOfFileSignatures); // Array(3) [ "ffd8ffdb", "ffd8ffe0", "47494638" ]
};
fileReader.readAsArrayBuffer(blob);
};
console.log(listOfFileSignatures); // Array []
Heres the output
You can declare listOfFileSignatures globally, but the signatures are computed asynchronously, so the list will be empty directly after the for loop. FileReader is always asynchronous, so you can't avoid that. One possibility to handle this is to check if the list is full inside onloadend (listOfFileSignatures.length == uploadedFiles.length) and then do what you want there.
A nicer approach is to use promises, like this:
var uploadedFiles = document.getElementById("notes").files;
Promise.all([...uploadedFiles].map(file => new Promise((resolve, reject) => {
var blob = file;
var fileReader = new FileReader();
fileReader.onloadend = function readFirstFourBytes(e) {
var arr = (new Uint8Array(e.target.result)).subarray(0, 4);
var fileSignature = "";
for (var i = 0; i < arr.length; i++) {
fileSignature += arr[i].toString(16);
};
resolve(fileSignature);
};
fileReader.readAsArrayBuffer(blob);
}))).then(function(listOfFileSignatures) {
// this will be called once, when all results are collected.
console.log(listOfFileSignatures);
});
Additionally, reading all bytes and then select just the first 4 byte is inefficient. Improved version:
Promise.all([...uploadedFiles].map(file => new Promise((resolve, reject) => {
var blob = file;
var fileReader = new FileReader();
fileReader.onloadend = function readFirstFourBytes(e) {
var arr = new Uint8Array(e.target.result);
var fileSignature = "";
for (var i = 0; i < arr.length; i++) {
fileSignature += arr[i].toString(16);
};
resolve(fileSignature);
};
fileReader.readAsArrayBuffer(blob.slice(0, 4));
}))).then(function(listOfFileSignatures) {
// this will be called once, when all results are collected.
console.log(listOfFileSignatures);
});
fileReader.onload is asynchronous, console.log (listOfFileSignatures); is called before files have been read
one option is to create an external function that returns a promise, returning the listOfFileSignatures array
function getListFile() {
return new Promise((resolve, reject) => {
var blob = file;
var fileReader = new FileReader();
fileReader.onloadend = function readFirstFourBytes(e) {
var arr = (new Uint8Array(e.target.result)).subarray(0, 4);
var fileSignature = "";
for (var i = 0; i < arr.length; i++) {
fileSignature += arr[i].toString(16);
};
listOfFileSignatures.push(fileSignature);
resolve(listOfFileSignatures);
};
}
}
I want to storage an array of objects in local storage but I just can storage one object into the array. All the new objects are overwriting the previous one in index 0.
This is my JavaScript code:
class Track {
constructor (title, genre) {
this.title = title;
this.genre = genre;
this.id = new Date().getTime();
this.creation = new Date();
}
}
class TrackList {
constructor () {
this.tracks = [];
}
newTrack(track) {
this.tracks.push(track);
this.saveLocalStorage();
}
saveLocalStorage() {
localStorage.setItem('tracks', JSON.stringify(this.tracks));
}
}
const addSongForm = document.querySelector('.addSongForm');
addSongForm.addEventListener('submit', (e) => {
const songTitle = document.querySelector('.songTitle').value;
const songGenre = document.querySelector('.songGenre').value;
const newTrackForm = new Track(songTitle, songGenre);
const trackList = new TrackList();
trackList.newTrack(newTrackForm);
});
Thanks in advance!!
get the current content of the local storage first and then put the new objects into the array.
var currentTracks = localStorage.getItem('tracks');
localStorage.setItem('tracks', JSON.stringify(JSON.parse(currentTracks).concat(this.tracks)));
EDIT: if the current objects that has the same ID as the new object need to be replaced, the new array needs to be adjusted.
/**
* source : https://www.cnblogs.com/water-1/p/10780528.html
*/
function mergeArray(arr1,arr2){
var _arr = new Array();
for(var i=0;i<arr1.length;i++){
_arr.push(arr1[i]);
}
for(var i=0;i<arr2.length;i++){
var flag = true;
for(var j=0;j<arr1.length;j++){
if(arr2[i]['id']==arr1[j]['id']){
flag=false;
break;
}
}
if(flag){
_arr.push(arr2[i]);
}
}
return _arr;
}
var currentTracks = JSON.parse(localStorage.getItem('tracks'));
localStorage.put('tracks', mergeArray(this.tracks, currentTracks));
i have this class (product).
var Product = function () {
this.products = [];
this.priceFrom = null;
this.priceTo = null;
this.countDone = 0;
};
Product.prototype = {
constructor: Product,
getProductsByPriceRange: function (priceFrom, priceTo) {
var xhrUrl = "<?= base_url('market/products/xhr_product_price_range') ?>";
var xhrData = {price_from: priceFrom, price_to: priceTo};
var xhrType = "json";
var UtilsClass = new Utils();
UtilsClass.xhrConnection(xhrUrl, xhrData, xhrType, function (data) {
/* MY DATA IS HERE */
});
},
buildList:function (products) {
for (var i = 0; i < products.length; i++) {
var product = products[i];
console.log("product");
}
},
buildOne: function (product) {
}
};
/*....more classes */
And another piece of code (out of product class):
var fromPrice = data.from;
var toPrice = data.to;
var ProductClass = new Product();
var lastCountDone = ProductClass.countDone;
ProductClass.priceFrom = fromPrice;
ProductClass.priceTo = toPrice;
var myProducts = ProductClass.getProductsByPriceRange(ProductClass.priceFrom, ProductClass.priceTo);
My question is... can i wait callback of UtilsClass.xhrConnection (in first piece) and use generated data of callback in second piece of code (out of first piece).
Any ideas would be very valuable to me. Thank you!
var Product = function () {
this.products = [];
this.priceFrom = null;
this.priceTo = null;
this.countDone = 0;
};
Product.prototype = {
constructor: Product,
getProductsByPriceRange: function (priceFrom, priceTo) {
var xhrUrl = "<?= base_url('market/products/xhr_product_price_range') ?>";
var xhrData = {price_from: priceFrom, price_to: priceTo};
var xhrType = "json";
var UtilsClass = new Utils();
return new Promise(function(resolve, reject){
UtilsClass.xhrConnection(xhrUrl, xhrData, xhrType, function (data) {
/* MY DATA IS HERE */
resolve(data)
});
});
},
buildList:function (products) {
for (var i = 0; i < products.length; i++) {
var product = products[i];
console.log("product");
}
},
buildOne: function (product) {
}
};
While calling,
var fromPrice = data.from;
var toPrice = data.to;
var ProductClass = new Product();
var lastCountDone = ProductClass.countDone;
ProductClass.priceFrom = fromPrice;
ProductClass.priceTo = toPrice;
var myProducts = ProductClass.getProductsByPriceRange(ProductClass.priceFrom, ProductClass.priceTo).then(function(data){%your data will be available here%});
How can I get the return value of that.whatever back from the closure? Instead of:
this.setCanvas = function(files){
var numItems = files.length - 1;
this.items = {};
var i = 0;
for(i=0;i<=numItems;i++)
{
var file = files[i];
var reader = new FileReader();
reader.onload = (function(i) {
return function(e) {
var something = that.whatever();
items[i] = something;
};
})(i);
reader.readAsDataURL(file);
}
console.log(items);
}
I need items[i] defined. If I console.log items[i] outside of the closure it is undefined.
The problem isn't the closure; it's the callback. Whatever needs to use the value of that.whatever needs to be executed in the callback.
You can augment your code to keep track of the number of files loaded. This way, when the last file has been loaded, you can invoke an ultimate completion handler:
this.setCanvas = function(files) {
var numItems = files.length - 1;
var itemsLoaded = 0; // Initialize to zero
var items = [];
var i = 0;
for(i=0;i<=numItems;i++) {
var file = files[i];
var reader = new FileReader();
reader.onload = (function(i) {
return function(e) {
var something = that.whatever();
items[i] = something;
if(++itemsLoaded == numItems) {
// At this point all files will have been loaded.
allLoaded();
}
};
})(i);
reader.readAsDataURL(file);
}
function allLoaded() {
// Now we can analyze the results
console.log(items);
}
}
I also changed items to be an Array instead of an Object.
Also if you want to be a little more clever, you could decrement numItems and check for zero instead of creating a new itemsLoaded variable.
There are a couple things you need to change here. First set this.items to an array. Next assign this to self so it can be referenced in the closure. Next assign the something to self.items[i] instead of items[i]. Finally use this.items in the console.log
this.setCanvas = function(files){
var self = this;
var numItems = files.length - 1;
this.items = [];
var i = 0;
for(i=0;i<=numItems;i++)
{
var file = files[i];
var reader = new FileReader();
reader.onload = (function(i) {
return function(e) {
var something = that.whatever();
self.items[i] = something;
};
})(i);
reader.readAsDataURL(file);
}
console.log(this.items);
}