Implementing a Preloader in Processingjs Canvas element - javascript

How do you implement a preloader for a processingjs canvas element?
Two cases - assets have loaded, but js still calculating/rendering the view; assets have not loaded
P.S. Someone create the processingjs tag! Users with rep lt 1500 can't do that yet :(

May be this very old snippet helps you, after loading all images a callback is called. The folder param was used to load low or highres pics.
pics = {
'Trans100' : "trans100.png",
'Trans101' : "trans101.png",
'TransPanel' : "transPanel.png"
}
function oAttrs(o) { var a, out = ""; for (a in o){ out += a + " ";} return trim(out);}
function imageLoader(pics, folder, onready){
this.nextPic = 0;
this.pics = pics;
this.folder = folder;
this.names = oAttrs(pics).split(" ");
this.onReady = onready;
this.loadNext();
}
imageLoader.prototype.loadNext = function (){
if (this.nextPic === this.names.length) {
this.onReady();
} else {
this.loadImage(this.pics[this.names[this.nextPic]]);
}
};
imageLoader.prototype.loadImage = function (file){
this.nextPic += 1;
var pic = new Image();
pic.onload = this.loadNext.bind(this);
pic.onerror = function(){console.error("ERROR: " + file);};
pic.src = this.folder + file;
};
// example:
new imageLoader(pics, "images", function(){
console.log("Images loaded");
})

Related

Loading multiple images with javascript

I am strugling to create a script to load multiple images for a game drawn on Canvas. The window seems to load without completing the load of all images. I've tried many ways but none of them seems to work. The function drawGameMenu() is called before the images are actually loaded and so the images are not drawn. If someone could help, I would be grateful. Here is my script, kind regards:
var imageNames = ["menuImage", "resetScoreButton", "instructionsButton", "playButton", "dialogPanel", "gamePlayImage", "exitButton", "timerPanel", "messengerPanel", "scoreBar", "yesButton", "noButton", "goButton"];
var imageFileNames = ["game_Menu", "reset_score_button", "instructions_button", "play_button", "dialog_panel", "game_play", "exit_button", "timer", "messenger_panel", "score_bar", "yes_button", "no_button", "go_button"];
var imageCollection = {};
window.addEventListener("load", function() {
var u = imageNames.length - 1;
for(i = 0; i <= u; i++) {
var name = imageNames[i];
imageCollection[name] = new Image();
imageCollection[name].src = imageFileNames[i] + ".png";
console.log(imageCollection[name]);
imageCollection[name].addEventListener('load', function() {
do {
var x = imageCollection[name].complete;
}
while(x != true);
});
}
drawGameMenu();
});
I made some changes on the script and now it works on the PC browser, but not working on smartphone. The script is the following:
window.addEventListener("load", async function loadImageCollection() {
var u = imageNames.length - 1;
for(i = 0; i <= u; i++) {
var name = imageNames[i];
imageCollection[name] = new Image();
imageCollection[name].src = imageFileNames[i] + ".png";
do {
await new Promise((resolve, reject) => setTimeout(resolve, 50));
x = imageCollection[name].complete;
console.log(x);
}
while(x == false);
}
drawGameMenu();
});
Keep it simple
Just use a simple callback and a counter to count of images as they load. Adding promises adds an additional level of complexity that is just a source of potential bugs. (the promise for each image and its callback and the need to call it on image load, and the need to handle promise.all with another callback)
const imageCollection = loadImages(
["menuImage", "resetScoreButton", "instructionsButton", "playButton", "dialogPanel", "gamePlayImage", "exitButton", "timerPanel", "messengerPanel", "scoreBar", "yesButton", "noButton", "goButton"],
["game_Menu", "reset_score_button", "instructions_button", "play_button", "dialog_panel", "game_play", "exit_button", "timer", "messenger_panel", "score_bar", "yes_button", "no_button", "go_button"],
drawGameMenu // this is called when all images have loaded.
);
function loadImages(names, files, onAllLoaded) {
var i = 0, numLoading = names.length;
const onload = () => --numLoading === 0 && onAllLoaded();
const images = {};
while (i < names.length) {
const img = images[names[i]] = new Image;
img.src = files[i++] + ".png";
img.onload = onload;
}
return images;
}
With the use of promises this becomes a very easy task. I don't know if ES6 allows it, but give it a try anyways.
var jarOfPromise = [];
for(i = 0; i <= u; i++) {
jarOfPromise.push(
new Promise( (resolve, reject) => {
var name = imageNames[i];
imageCollection[name] = new Image();
imageCollection[name].src = imageFileNames[i] + ".png";
console.log(imageCollection[name]);
imageCollection[name].addEventListener('load', function() {
resolve(true);
});
})
)
}
Promise.all(jarOfPromise).then( result => {
drawGameMenu();
});

Photoshop javascript batch replace smart layer from folder and resize

I am trying to work out how to use javascript with photoshop, but eventhough i dont find a logical error in the code, it doesnt work properly.
I have a folder of 1000+ images/.ai files that have varying dimensions. I need these images on the Pillow and saved as .jpeg.
I choose the smartlayer and run the script to choose the images and it saves them correctly. The only problem is, that the resizing of images and positioning dont work properly.
If i put the image in manually, it works without issues, but not with the script.
If the width is greater than the height, it should set the width to 1200 px and calculate the height according to that. (and vice versa) and place in the middle of the layer.
How do i fix the resizing and positioning?
Is it possible to choose a folder where the images are inside instead of selecting the images?
How do i handle it when there are 2 smart layers to change in the mockup instead of 1?
Anyone know where the problem lies this code?
Im grateful for any bit of help!
// Replace SmartObject’s Content and Save as JPG
// 2017, use it at your own risk
// Via #Circle B: https://graphicdesign.stackexchange.com/questions/92796/replacing-a-smart-object-in-bulk-with-photoshops-variable-data-or-scripts/93359
// JPG code from here: https://forums.adobe.com/thread/737789
#target photoshop
if (app.documents.length > 0) {
var myDocument = app.activeDocument;
var theName = myDocument.name.match(/(.*)\.[^\.]+$/)[1];
var thePath = myDocument.path;
var theLayer = myDocument.activeLayer;
// JPG Options;
jpgSaveOptions = new JPEGSaveOptions();
jpgSaveOptions.embedColorProfile = true;
jpgSaveOptions.formatOptions = FormatOptions.STANDARDBASELINE;
jpgSaveOptions.matte = MatteType.NONE;
jpgSaveOptions.quality = 8;
// Check if layer is SmartObject;
if (theLayer.kind != "LayerKind.SMARTOBJECT") {
alert("selected layer is not a smart object")
} else {
// Select Files;
if ($.os.search(/windows/i) != -1) {
var theFiles = File.openDialog("please select files", "*.psd;*.tif;*.jpg;*.ai", true)
} else {
var theFiles = File.openDialog("please select files", getFiles, true)
};
};
(function (){
var startRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
var bounds = activeDocument.activeLayer.bounds;
var height = bounds[3].value - bounds[1].value;
var width = bounds[2].value - bounds[0].value;
if (height > width){
var newSize1 = (100 / width) * 800;
activeDocument.activeLayer.resize(newSize1, newSize1, AnchorPosition.MIDDLECENTER);
app.preferences.rulerUnits = startRulerUnits;
}
else{
var newSize2 = (100 / height) * 800;
activeDocument.activeLayer.resize(newSize2, newSize2, AnchorPosition.MIDDLECENTER);
app.preferences.rulerUnits = startRulerUnits;
}
})();
if (theFiles) {
for (var m = 0; m < theFiles.length; m++) {
// Replace SmartObject
theLayer = replaceContents(theFiles[m], theLayer);
var theNewName = theFiles[m].name.match(/(.*)\.[^\.]+$/)[1];
// Save JPG
myDocument.saveAs((new File(thePath + "/" + theName + "_" + theNewName + ".jpg")), jpgSaveOptions, true,Extension.LOWERCASE);
}
}
};
// Get PSDs, TIFs and JPGs from files
function getFiles(theFile) {
if (theFile.name.match(/\.(psd|tif|jpg)$/i) != null || theFile.constructor.name == "Folder") {
return true
}
};
// Replace SmartObject Contents
function replaceContents(newFile, theSO) {
app.activeDocument.activeLayer = theSO;
// =======================================================
var idplacedLayerReplaceContents = stringIDToTypeID("placedLayerReplaceContents");
var desc3 = new ActionDescriptor();
var idnull = charIDToTypeID("null");
desc3.putPath(idnull, new File(newFile));
var idPgNm = charIDToTypeID("PgNm");
desc3.putInteger(idPgNm, 1);
executeAction(idplacedLayerReplaceContents, desc3, DialogModes.NO);
return app.activeDocument.activeLayer
};
I have attached 2 pictures. 1 how it needs to look like and 2 what the script outputs
Correct
Wrong
Your replaced images have to be the same resolution as the smart object.
You can declare the folder path in your code. If you still want to select the path by hand, you can select one image in the path, and extract the parent folder path.
You can recursively go through all of the layers in the documents and extract all the smart objects that you want to replace.
You may want a function to recursively traverse all the layers in the document
function browseLayer(layer, fn) {
if (layer.length) {
for (var i = 0; i < layer.length; ++i) {
browseLayer(layer[i], fn)
}
return;
}
if (layer.layers) {
for (var j = 0; j < layer.layers.length; ++j) {
browseLayer(layer.layers[j], fn);
}
return;
}
//apply this function for every layer
fn(layer)
}
Get all the smart objects in the document
const smartObjects = [];
//The smart objects can be visit twice or more
//use this object to mark the visiting status
const docNameIndex = {};
const doc = app.open(new File("/path/to/psd/file"));
browseLayer(doc.layers, function (layer) {
//You cannot replace smart object with position is locked
if (layer.kind == LayerKind.SMARTOBJECT && layer.positionLocked == false) {
smartLayers.push(layer);
doc.activeLayer = layer;
//open the smart object
executeAction(stringIDToTypeID("placedLayerEditContents"), new ActionDescriptor(), DialogModes.NO);
//activeDocument is now the smart object
const docName = app.activeDocument.name;
if (!docNameIndex[docName]) {
docNameIndex[docName] = true;
smartObjects.push({
id: layer.id,
name: layer.name,
docName: docName,
width : app.activeDocument.width.as('pixels'),
height : app.activeDocument.height.as('pixels'),
resolution : app.activeDocument.resolution //important
});
}
//reactive the main document
app.activeDocument = doc;
}
});
I assume that you have two smart objects needed to replace, the images for replacement are stored in different folders with the same name.
smartObjects[0].replaceFolderPath = "/path/to/folder/1";
smartObjects[1].replaceFolderPath = "/path/to/folder/2";
//we need temp folder to store the resize images
smartObjects[0].tempFolderPath = "/path/to/temp/folder/1";
smartObjects[1].tempFolderPath = "/path/to/temp/folder/2";
Ex: The first iteration will replace smartObjects[0] with "/path/to/folder/1/image1.jpg", and smartObjects[1] with "/path/to/folder/image1.jpg"
Now resize all the images following the properties of the smart objects
smartObjects.forEach(function(smartObject){
//Get all files in the folder
var files = new Folder(smartObject.replaceFolderPath).getFiles();
//Resize all the image files
files.forEach(function (file) {
var doc = app.open(file);
doc.resizeImage(
new UnitValue(smartObject.width + ' pixels'),
new UnitValue(smartObject.height + ' pixels'),
smartObject.resolution
);
//save to temp folder
doc.saveAs(
new File(smartObject.tempFolderPath + "/" + file.name),
new PNGSaveOptions(),
true
);
doc.close(SaveOptions.DONOTSAVECHANGES)
});
});
Finally, replace the smart object
//get list of file again
var files = new Folder(smartObject.replaceFolderPath).getFiles();
files.forEach(function(file){
var fileName = file.name;
smartObjects.forEach(function(smartObject){
//active the window opening the smart object
app.activeDocument = app.documents.getByName(args.documentName);
var desc = new ActionDescriptor();
desc.putPath(charIDToTypeID("null"), new File(smartObject.tempFolderPath + "/" + fileName));
executeAction(stringIDToTypeID( "placedLayerReplaceContents" ), desc, DialogModes.NO);
});
//Now export document
var webOptions = new ExportOptionsSaveForWeb();
webOptions.format = SaveDocumentType.PNG; // SaveDocumentType.JPEG
webOptions.optimized = true;
webOptions.quality = 100;
doc.exportDocument(new File("/path/to/result/folder" + file.name), ExportType.SAVEFORWEB, webOptions);
});
Now you can close all the opening smart objects
smartObjects.forEach(function (s) {
app.documents.getByName(r.docName).close(SaveOptions.DONOTSAVECHANGES);
});

Using PreloadJS to load images and adding them to CreateJS stage

I'm trying to create a Tri Peaks Solitaire game in Javascript, using CreateJS to help interact with the HTML canvas. I'm not entirely new to programming, as I've taken college courses on Java and C, but I am new to both Javascript and HTML.
I created a large amount of the card game using a Card class and Deck class, with the image adding, event listening, and game logic done in one Javascript file, but this resulted in a lot of messy, somewhat buggy code that I felt needed cleaning up.
I'm trying to implement a separate class for the card table, which will be the canvas, or stage, and it will draw the 4 rows of playable cards, the stock pile, and the waste pile. I'm currently just trying to get the stock pile to show up on the canvas, but it's not happening.
My question is, why is my stock pile not showing up? Also, as you can see, the card images are being loaded when the cards are created. Should I be doing that differently?
Card class:
var Card = function(number) {
this.isFaceUp = false;
//number for image file path
this.number = number;
this.value = Math.ceil( (this.number)/4 );
//back of the card is default image
this.initialize("images/" + 0 + ".png");
console.log("card created")
this.height = this.getBounds().height;
this.width = this.getBounds().width;
//load the card's front image
this.frontImage = new Image();
this.frontImage.src = ( this.getImagePath() );
};
Card.prototype = new createjs.Bitmap("images/" + this.number + ".png");
Card.prototype.getImagePath = function() {
return "images/" + this.number + ".png";
};
Card.prototype.getValue = function () {
return this.value;
}
Card.prototype.flip = function() {
// this.image = new Image();
// this.image.src = ( this.getImagePath() );
this.image = this.frontImage;
this.isFaceUp = true;
};
Card.prototype.getHeight = function() {
return this.height;
};
Card.prototype.getWidth = function() {
return this.width;
};
CardDeck class:
function CardDeck() {
//create empty array
this.deck = [];
//fill empty array
for(i=1; i<=52; i++) {
//fill deck with cards, with i being the number of the card
this.deck[i-1] = new Card(i);
}
//shuffle deck
this.shuffle();
}
CardDeck.prototype.getCard = function() {
if(this.deck.length < 1) alert("No cards in the deck");
return this.deck.pop();
};
CardDeck.prototype.getSize = function() {
return this.deck.length;
};
CardDeck.prototype.shuffle = function() {
for (i=0; i<this.deck.length; i++) {
var randomIndex = Math.floor( Math.random()*this.deck.length );
var temp = this.deck[i];
this.deck[i] = this.deck[randomIndex];
this.deck[randomIndex] = temp;
}
};
CardDeck.prototype.getRemainingCards = function() {
return this.deck.splice(0);
}
CardDeck.prototype.listCards = function() {
for(i=0; i<this.deck.length; i++) {
console.log(this.deck[i].number);
}
};
CardTable class:
var CardTable = function(canvas) {
this.initialize(canvas);
this.firstRow = [];
this.secondRow = [];
this.thirdRow = [];
this.fourthRow = [];
this.stockPile = [];
this.wastePile = [];
//startX is card width
this.startX = ( new Card(0) ).width*3;
//startY is half the card height
this.startY = ( new Card(0) ).height/2;
};
CardTable.prototype = new createjs.Stage();
CardTable.prototype.createStockPile = function(cards) {
for(i=0; i<23; i++) {
cards[i].x = 10;
cards[i].y = 50;
this.stockPile[i] = cards[i];
}
};
CardTable.prototype.addToWastePile = function(card) {
card.x = startX + card.width;
card.y = startY;
this.wastePile.push(card);
this.addChild(card);
this.update();
};
CardTable.prototype.drawStockPile = function() {
for(i=0; i<this.stockPile.length; i++) {
console.log("this.stockPile[i]");
this.addChild(this.stockPile[i]);
}
this.update();
};
HTML file:
<html>
<head>
<title>Tri-Peaks Solitaire</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://code.createjs.com/easeljs-0.8.1.min.js"></script>
<script src="card.js"></script>
<script src="carddeck.js"></script>
<script src="cardtable.js"></script>
<script>
function init(){
var canvas = document.getElementById("canvas");
var table = new CardTable("canvas");
deck = new CardDeck();
table.createStockPile(deck.getRemainingCards());
table.drawStockPile();
table.update();
}
</script>
</head>
<body onload="init()">
<h1>Tri-Peaks Solitaire</h1>
<canvas style='border: 5px solid green; background: url("images/background.jpg");'
id="canvas" width="1000" height="450">
</canvas>
</body>
</html>
PRELOADJS UPDATE:
var canvas = document.getElementById("canvas");
var stage = new createjs.Stage(canvas);
var gameDeck = new CardDeck();
var numbers = [];
var preloader;
var progressText = new createjs.Text("", "20px Arial","#FF0000");
progressText.x = 50;
progressText.y = 20;
stage.addChild(progressText);
stage.update();
setupManifest();
startPreload();
function setupManifest() {
for(i=0; i<=52; i++) {
console.log("manifest");
manifest.push( {src:"images/" + i + ".png",
id: i} );
numbers.push(i);
}
}
function startPreload() {
preload = new createjs.LoadQueue(true);
preload.on("fileload", handleFileLoad);
preload.on("progress", handleFileProgress);
preload.on("complete", loadComplete);
preload.on("error", loadError);
preload.loadManifest(manifest);
}
function handleFileLoad(event) {
console.log("A file has loaded of type: " + event.item.type);
//console.log(numbers.pop());
var cardNumber = numbers.pop();
var newCard = new Card(cardNumber);
//faces.push( new Image() );
//faces[ faces.length - 1 ].src = "images/" + cardNumber + ".png";
gameDeck.insertCard(newCard);
}
function loadError(evt) {
console.log("error",evt.text);
}
function handleFileProgress(event) {
progressText.text = (preload.progress*100|0) + " % Loaded";
stage.update();
}
function loadComplete(event) {
console.log("Finished Loading Assets");
}
You are only updating the stage immediately after creating the cards. The cards are loading bitmap images, which is a concurrent operation that takes time. As such, the only time you render the stage is before the images load.
I would suggest using PreloadJS to preload your card images. This will also give you a lot more control over when / how they load, and let you show a progress bar or distractor to the user as they load.
I would suggest waiting for all the images to load and then calling stage.update(). Alternatively use the createjs.Ticker.addEventListener("tick", handleTick); event handler to update the stage for you.
Reference: http://www.createjs.com/docs/easeljs/classes/Ticker.html

Use javascript to write html around loop

I'm using the javascript from this older script: JavaScript: how to load all images in a folder?
However, I want to use the same javascript file to write some html code before the loop and some after the loop, all using this same javascript file.
Just putting a document.write before and after it in the javascript doesn't work.
var bCheckEnabled = true;
var bFinishCheck = false;
var img;
var imgArray = new Array();
var i = 0;
var myInterval = setInterval(loadImage, 1);
function loadImage() {
if (bFinishCheck) {
clearInterval(myInterval);
document.write('Loaded ' + i + ' image(s)');
return;
}
if (bCheckEnabled) {
bCheckEnabled = false;
img = new Image();
img.onload = fExists;
img.onerror = fDoesntExist;
img.src = 'assets/' + i + '.png';
document.write('<img src="assets/' + i + '.png" width="1016" height="813" alt="Kar" />');
}
}
function fExists() {
imgArray.push(img);
i++;
bCheckEnabled = true;
}
function fDoesntExist() {
bFinishCheck = true;
}
You may be using this example code incorrectly. Its intent appears to be to pre-load the image files in the background, not display them to the user in the HTML (yet). This is to improve apparent download speed of your page.
In short, this code doesn't actually render the image into the DOM; and that is intentional behavior.

Generic Javascript Image Swap

I'm building a navigation bar where the images should be swapped out on mouseover; normally I use CSS for this but this time I'm trying to figure out javascript. This is what I have right now:
HTML:
<li class="bio"><img src="images/nav/bio.jpg" name="bio" /></li>
Javascript:
if (document.images) {
var bio_up = new Image();
bio_up.src = "images/nav/bio.jpg";
var bio_over = new Image();
bio_over.src = "images/nav/bio-ov.jpg";
}
function over_bio() {
if (document.images) {
document["bio"].src = bio_over.src
}
}
function up_bio() {
if (document.images) {
document["bio"].src = bio_up.src
}
}
However, all of the images have names of the form "xyz.jpg" and "xyz-ov.jpg", so I would prefer to just have a generic function that works for every image in the navbar, rather than a separate function for each image.
A quick-fire solution which should be robust enough provided all your images are of the same type:
$("li.bio a").hover(function() {
var $img = $(this).find("img");
$img[0].src = $img[0].src.replace(".jpg", "") + "-ov.jpg";
}, function() {
var $img = $(this).find("img");
$img[0].src = $img[0].src.replace("-ov.jpg", "") + ".jpg";
});
This should work will all image formats as long as the extension is between 2 and 4 characters long IE. png, jpeg, jpg, gif etc.
var images = document.getElementById('navbar').getElementsByTagName('img'), i;
for(i = 0; i < images.length; i++){
images[i].onmouseover = function(){
this.src = this.src.replace(/^(.*)(\.\w{2,4})$/, '$1'+'-ov'+'$2');
}
images[i].onmouseout = function(){
this.src = this.src.replace(/^(.*)-ov(\.\w{2,4})$/, '$1'+'$2');
}
}
Here's an idea in plain javascript (no jQuery):
function onMouseOverSwap(e) {
e.src = e.src.replace(/\.jpg$/", "-ov.jpg"); // add -ov onto end
}
function onMouseOutSwap(e) {
e.src = e.src.replace(/(-ov)+\.jpg$/, ".jpg"); // remove -ov
}

Categories

Resources