Updating Canvas Image dynamically using PaperJs - image

I have an image element with src pointing to an Handler (asp.net), and this image being used as source for Raster (PaperJs) object defined as global object and updated during window.load.
var raster;
paper.install(window);
function WindowOnLoad() {
raster = new paper.Raster('MainContent_imageData');
raster.position = paper.view.center;
window.paper.view.draw();
}
The above code is loading image onto the canvas on first load then the image element is getting updated through a button click which is associated with callback control (ComponentArt callback control) but the canvas is not, it displays blank.
Then I created an handler that is being called after the callback is complete but it also didn't work.
function CallBackCompleted(sender,eventArgs) {
var imgData = document.getElementById('MainContent_imageData');
raster.image = imgData ;
raster.position = window.paper.view.center;
window.paper.view.draw();
}

The following code fixed the issue. The raster object is added as a child object on layer[0] removed all the other objects except children[0], which refers to the raster object.
function CallBackCompleted(sender,eventArgs) {
if(window.paper.project.layers[0].hasChildren())
window.paper.project.layers[0].removeChildren(1);
var imageObj = new Image();
imageObj.src = document.getElementById('MainContent_imageData').src;
imageObj.onload = function () {
window.paper.project.layers[0].children[0].setImage(imageObj);
window.paper.project.layers[0].children[0].position = window.paper.view.center;
window.paper.view.draw();
}
}

Related

Canvas ctx.drawImage is not working with a transparent PNG

ctx.drawImage() is not working when I use a transparent PNG, but does work when I use a regular PNG.
var context = document.getElementById("canvas").getContext('2d');
....
function draw(img, x, y){
context.drawImage(img, x, y);
}
//actulaly there is loop here, but for simplicity I put only this.
var img = new Image();
img.src = "images/a.png";
img.onload = draw(img, 10, 10);
If I use a regular PNG image it works, but with a PNG with transparency that has the background deleted, it is not working.
Do you guys have any idea why? Thank you.
img.onload takes a function reference rather than a function call.
So do this:
img.onload=function(){draw(img,10,10);}
If you need to load many images, here is an image preloader that fully loads all images before calling the start() function:
// image loader
// put the paths to your images in imageURLs[]
var imageURLs=[];
// push all your image urls!
imageURLs.push("");
imageURLs.push("");
// the loaded images will be placed in images[]
var imgs=[];
var imagesOK=0;
loadAllImages(start);
function loadAllImages(callback){
for (var i=0; i<imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function(){
imagesOK++;
if (imagesOK>=imageURLs.length ) {
callback();
}
};
img.onerror=function(){alert("image load failed");}
img.crossOrigin="anonymous";
img.src = imageURLs[i];
}
}
function start(){
// the imgs[] array now holds fully loaded images
// the imgs[] are in the same order as imageURLs[]
}

optimizing code for future changes

I am a bit new to this but one of my canvas slide shows begins with the creation of image objects whuch are later inserted into an arra.
var image0 = new Image();
image0.src = "images/pic1.jpg";
var image1 = new Image();
image1.src = "images/pic2.jpg";
var image2 = new Image();
image2.src = "images/pic3.jpg";
var image3 = new Image();
image3.src = "images/pic4.jpg";
// array of 4 images
images = new Array(image0, image1, image2, image3);
however, whenever i try to put it in a "for" loop so that later on i can add pictures to it, the code crushes, any idea what the best syntax for this wil be?
var im = new Array(x);
for (var i = 1;i<im.length;i++) {
im[i] = new Image;
im[i].src = "images/pic"+(i+1)+".jpg"
};
images = new Array(im[0], im[1], im[2], im[3]);
i apologize in advance if the answer is too simple and i just missed it.
The problem with the code is that as it stands the number of images is unknown at the time the loop starts.
One way is to define the urls you want to use first.
var urls = [url1, url2, url3, url3]; /// no need for new Array()
Then create another array to hold the images. This can be empty when we start as we will fill it with image elements:
var images = [], /// array to hold images
count = urls.length; /// counter
The next problem is that your code does not handle the asynchronous nature of image loading. We will have to add a load handler to the element so we know when it has finished loading all the images. We can share the same handler for all images by using a counter so:
for(var i = 0; i < urls.length; i++) {
var img = new Image; /// declare the var in here to avoid same reference
images.push(img); /// add the element to the images array
img.onload = handle; /// set load handler for element
img.src = urls[i]; /// and finally set src which will start the loading
}
What happens here is that the code continues when the loop has finished as the images are still loading in the background. So what we need to do is to continue from the handler when all images has loaded:
function handler() {
count--; /// count-down for this load
if (count === 0) {
/// here all images has loaded so from here we call the next function
/// in the code
nextStep(); /// some function to invoke next
}
}
The bonus here is that your images array has the images in the same order as in the url array.
As mentioned elsewhere there are many loaders out there. One of them is my own YAIL loader (MIT license = free) which you can check out if you want less hassle with loading many images. It does all the above and a bit more such as handling errors and progress++.

Flex 4.6 Add Images programatically to a View's elements List with single update cycle

I want to add Images into a s:View on runtime. However, I need them to be added on override of the data to optimize the apearance of the view. However, my container, the s:View, is not yet added into the display list or created I am not sure since I have a hard time understand the lifecycle and I am new in Flex 4.6. Anyway, the container is not yet instanciated. So how do I add the Images to the Elements list so that when the View is created it adds them as elements.
Basically same way that it happens when you write them on the mxml.
Thanks,
Dave
You could do something like this:
var v:View = new View();
v.layout = new VerticalLayout();
var img:Image = new Image();
img.source = "/path/to/image1";
v.addElement(img);
img = new Image();
img.source = "/path/to/image2";
v.addElement(img);
img = new Image();
img.source = "/path/to/image3";
v.addElement(img);
this.addElement(v);
I had to add the elements not on override of data but on createChildren.
override public function set data(value:Object):void
{
super.data = value;
if(value == null)
{
//initialize the data with the images I need to cache
}
}
override protected function createChildren():void
{
super.createChildren();
if(container && definition)//These are the components I need to have instanciated
{
//I then use the cached Images I initialized on the override of data
}
}

Have embedded images open with FancyBox, not in new window

This section of code in my /js/global.js file activate each image to open in a new window when clicked. Is it possible to alter this code to have each open in a FancyBox instead? I have downloaded a FancyBox plugin for a Vanilla forum I am running, and it currently only targets images embedded in posts After You Click On The Post Itself. On the main page, clicking on an image opens a new window.
// Shrink large images to fit into message space, and pop into new window when clicked.
// This needs to happen in onload because otherwise the image sizes are not yet known.
jQuery(window).load(function() {
var props = ['Width', 'Height'], prop;
while (prop = props.pop()) {
(function (natural, prop) {
jQuery.fn[natural] = (natural in new Image()) ?
function () {
return this[0][natural];
} :
function () {
var
node = this[0],
img,
value;
if (node.tagName.toLowerCase() === 'img') {
img = new Image();
img.src = node.src,
value = img[prop];
}
return value;
};
}('natural' + prop, prop.toLowerCase()));
}
jQuery('div.Message img').each(function(i,img) {
var img = jQuery(img);
var container = img.closest('div.Message');
if (img.naturalWidth() > container.width() && container.width() > 0) {
img.wrap('');
}
});
// Let the world know we're done here
jQuery(window).trigger('ImagesResized');
});
Add an specific class to your wrapped images, modifying this line
img.wrap('');
... into this :
img.wrap('<a class="fancybox" href="'+$(img).attr('src')+'"></a>');
Then bind fancybox to that selector (".fancybox") in a custom script like :
$(".fancybox").fancybox();
This assumes that you have properly loaded the fancybox js and css files.

How to know which one fired eventListener

I am loading images for a game before the game begin. So the main function sends links of images to an image loading object. This is what happen in my image loading object when I do image.load(link) in my main :
public function charge(str:String, img_x:int, img_y:int, layer:Sprite):void
{
trace("load");
urlRequest = new URLRequest(str);
loaderArray[cptDemande] = new Loader();
loaderArray[cptDemande].contentLoaderInfo.addEventListener(Event.COMPLETE, loading_done);
loaderArray[cptDemande].load(urlRequest);
posX[cptDemande] = img_x;
posY[cptDemande] = img_y;
layerArray[cptDemande] = layer;
cptDemande++;
}
The parameters img_x:int, img_y:int and layer:Sprite are related to displaying the images afterward. I am using arrays to be able to add the images to the stage when the loading is all done.
The event listener fire this function :
public function loading_done(evt:Event):void
{
cptLoaded++;
evt.currentTarget.contentLoaderInfo.removeEventListener(Event.COMPLETE, loading_done);
if((cptDemande == cptLoaded) && (isDone == true))
{
afficher();
}
}
what I want is to be able to target the good loader to remove the event listener. What I am currently using(evt.currentTarget) doesn't work and generate an error code :
1069 Property data not found on flash.display.LoaderInfo and there is no default value
Tracing evt.currentTarget shows that currentTarget is the LoaderInfo property. Try updating your code as follows:
public function loading_done(evt:Event):void
{
cptLoaded++;
// Current target IS the contentLoaderInfo
evt.currentTarget.removeEventListener(Event.COMPLETE, loading_done);
//evt.currentTarget.contentLoaderInfo.removeEventListener(Event.COMPLETE, loading_done);
if((cptDemande == cptLoaded) && (isDone == true))
{
afficher();
}
}
Just a wee tip for you while I'm at it, you could make life a lot easier for yourself by storing all the properties of your images on an Object and then pushing these onto a single Array, rather than managing a separate Array for each property.
Something like this:
private var loadedImages:Array = new Array();
public function charge(str:String, img_x:int, img_y:int, layer:Sprite):void
{
var urlRequest:URLRequest = new URLRequest(str);
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loading_done);
loader.load(urlRequest);
var imageData:Object = { };
imageData.loader = loader;
imageData.posX = img_x;
imageData.posY = img_y;
imageData.layer = layer;
// Now we have a single Array with a separate element for
// each image representing all its properties
loadedImages.push(imageData);
cptDemande++;
}

Resources