How do I use an image to fill a layer in OpenLayers? - javascript

I'm using OpenLayers to draw a map, and I'd like to fill the polygon that describes a country with a photo from that country. I know I can pass a CanvasPattern to ol.style.Fill's color, but I don't know to generate an image.
Here's my code, and a JSfiddle with a full example: http://jsfiddle.net/07366L44/5/
function getPhoto() {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var photo = new Image();
photo.src = 'http://i.imgur.com/C6PL9tB.jpg';
return context.createPattern(photo, 'repeat');
}
I think it's because the photo is loaded async. Other examples create the pattern in the photo.onload method but I don't know how to return that.
Optimally I'd like to return a canvas which I can animate or update later with other photos, but I'll settle for just a single static image per country!

You'll have to set your style's fill color in the image's onload handler.
/**
* #param {ol.style.Style} style Style to set the pattern on.
*/
function setPattern(style) {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var photo = new Image();
photo.onload = function() {
canvas.width = photo.width;
canvas.height = photo.height;
var pattern = context.createPattern(photo, 'repeat');
style.getFill().setColor(pattern);
}
photo.src = 'http://i.imgur.com/C6PL9tB.jpg';
}
Note that the fill's color needs to be set before the style function is called, so you cannot call the setPattern function in the style function. It's better to create the style before loading the layer.

Related

Canva image dont display the image sometime using drawImage() method? [duplicate]

This question already has an answer here:
CanvasContext2D drawImage() issue [onload and CORS]
(1 answer)
Closed 3 years ago.
I want just to draw an image on canva using drawImage() method .But it happen sometime that the image is not drawn . even when I use the onload event it still fail to display the image.
Before I ask my question I want just to precise that the image is drawn somtimes and not drawn sometime .So what is the reason of the this problem and how can I tackle or fix it .
let imagez = new Image();
imagez.src="photo/run.png";
context.drawImage(imagez,10,10,50,60); // it does not draw the image always. why?
I expect the image to be drawn whenever I refresh the page
That most likely happens because the drawImage() method is called before the actual image is completely loaded.
Wait for it to finish loading before you call drawImage().
var canvas = document.createElement("canvas");
document.body.appendChild(canvas);
canvas.width = 400;
canvas.height = 300;
var context = canvas.getContext("2d");
let imagez = new Image();
imagez.onload = function() {
context.drawImage(imagez, 10, 10, 50, 60);
}
imagez.src = "https://picsum.photos/400/300";
For this you have to wait for the image(s) to load into memory before they are available to draw to the canvas.
A simple loadIMAGE() function to achieve this might look something like this...
function loadIMAGE(path, x, y, w, h) {
var img = new Image();
img.addEventListener("load", function(e) {
// 'this' === the image Object
context.drawImage(this, x, y, w, h);
}, !1);
img.src = path;
}
// USAGE
loadIMAGE("photo/run.png", 10, 10, 50, 60);
It is important that the event-Handling function is defined before the src value is assigned.
Hope that helped :)
You are attempting to draw the image without knowing if it was successfully loaded...
Here is a working sample, using the onload event
const canvas = document.getElementById("gameArea");
context = canvas.getContext("2d");
let imagez = new Image();
imagez.onload = function () {
context.drawImage(imagez, 10, 10, 50, 60);
}
imagez.src = "https://upload.wikimedia.org/wikipedia/commons/e/e2/3D_Gyroscope.png";
<canvas id=gameArea></canvas>

Conver konvajs canvas to image and load these image to another canvas

I tried to build many konvajs canvas and transform them to konva images.
And finally load these images to another canvas and show.
function main(){
var total_konva_stage= createStage();
var imagesLayer= new Konva.Layer();
for (var key in array){
$(".tmpcontainer").remove();
containerName = containerNameBase + key;
$("#hidden_div").append("<div id='"+containerName+"' class='tmpcontainer'></div>");
var konva_stage =createcanvas(containerName); //create a hidden dynamic div and bind this stage to it
var img = konva_stage .toDataURL("image/png");
var imageObj = new Image();
imageObj.src = img
imageObj.onload = function() {
callback_canvastoImg(imagesLayer, imageObj);
};
}
total_konva_stage.add(imagesLayer);
total_konva_stage.show();
total_konva_stage.draw();
}
function callback_canvastoImg(imagesLayer, imageObj){
var konva_image = new Konva.Image({imageObj});
imagesLayer.add(konva_image );
}
What I think my results is that "total_konva_stage" will have every separate img that from konva_stage.
But I found my second img will contain the first img , my third img contain the first, second img
I have no idea now, please help.
Thanks
onload function callback is asynchronous. It means it will be executed only after the whole array is processed.
So in this line: callback_canvastoImg(imagesLayer, imageObj); variable imageObj will refer to the LAST created image.
To avoid this you can:
Use modern js syntax of declaring variables (in case you should use something like babel)
const imageObj = new Image();
You can define closure function for loop iteration. You just need to use array.forEach instead of for loop:
array.forEach(function(item, key) {
$(".tmpcontainer").remove();
containerName = containerNameBase + key;
$("#hidden_div").append("<div id='"+containerName+"' class='tmpcontainer'></div>");
var konva_stage =createcanvas(containerName); //create a hidden dynamic div and bind this stage to it
var img = konva_stage .toDataURL("image/png");
var imageObj = new Image();
imageObj.src = img
imageObj.onload = function() {
callback_canvastoImg(imagesLayer, imageObj);
};
}

KonvaJS, masking instead of clipFunc possible?

I'm using konvajs and need some help!
Assume that I need an image that's draggable inside a complex shape.
I wonder if it's possible to use masking with Konva.Group instead of clipFunc OR a way to convert a masking image to canvas-clip-path and use it with clipFunc!
Like this: Masking draggable
By default Konva supports only simple clip with rectangle shape and clipping with clipFunc where you can describe required path.
https://konvajs.github.io/docs/clipping/Clipping_Function.html
In both cases, clipping is defined as canvas paths, and you can't define clip here as an image.
But you can draw directly into the canvas with custom Konva.Shape.
const girafe = new Image();
girafe.onload = () => {
const img = new Image();
img.onload = () => {
const image = new Konva.Shape({
sceneFunc: (ctx) => {
ctx.drawImage(girafe, 0, 0);
ctx.globalCompositeOperation = 'source-in';
ctx.drawImage(img, 0, 0);
},
// (!) important
// for this case you need to define custom hit function
hitFunc: (ctx) => {
ctx.rect(0, 0, girafe.width, girafe.height);
ctx.fillStrokeShape(image);
},
draggable: true
});
layer.add(image);
layer.draw();
};
img.src = "http://i.imgur.com/kKjW3U4.png";
}
girafe.src = "http://i.imgur.com/fQX2P8S.png";
The output will be:
DEMO: http://jsbin.com/qahulidube/2/edit?js,output
Note: remember to define hitFunc because Konva hit detection will not work for such sceneFunc
Another two demos with other behaviors:
http://jsbin.com/huserozire/1/edit?js,output
http://jsbin.com/hawiricaqu/1/edit

When to load an image to use the Javascript function drawImage()?

I would like to use the drawImage() function to load an image in a Javascript animation, but the image does not load with my current code. I think I need to specifically ask for the image to be loaded at some point, but I'm not sure when or how. The idea is to make a cloud that goes across the canvas. Thank you for your help.
function draw(x,y){
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
x += 2;
if(x>1000){
x=0;
}
var cloud = new image();
cloud.src='small_cloud.png';
ctx.drawImage(cloud,x,0);
var loopTimer = setTimeout('draw('+x+','+y+')',20);
}
Try new Image()rather than new image()
Also move
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var cloud = new image();
cloud.src='small_cloud.png';
to outside the draw()function, as you only want to set these things up once. Delay this setting up until the browser has had time to parse the document, for example using
window.onload = function(){ ... };
(Though in reality you'd have to make sure not to overwrite any existing window.onload listeners by using proper even handler registration.) Also delay the first invocation to draw()until the image has loaded, for example like so:
cloud.onload = function(){
draw(cloud, 0, 0);
}
I'd also change
var loopTimer = setTimeout('draw('+x+','+y+')',20);
to
setTimeout(function(){ draw(x, y); }, 20);
since you seem not to be using the loopTimer variable, and since passing setTimeout an anonymous function rather than a string is considered better practice.
Finally, it would look like this:
window.onload = function() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var cloud = new image();
cloud.src='small_cloud.png';
cloud.onload = function(){
draw(ctx, cloud, 0, 0);
};
};
function draw(ctx, cloud, x,y){
x += 2;
if(x>1000){
x=0;
}
ctx.drawImage(cloud,x,0);
setTimeout(function(){ draw(ctx, cloud, x, y); }, 20);
}
Note that, since ctx is no longer defined inside the draw() function, as it is local to the anonymous window.onload function, you also has to pass ctx to the draw() function.

drawImage() not drawing pre-loaded image object. Using OO javascript

I'm writing a simple 2D side scroller game using JavaScript + Canvas. I am trying to do this while learning OO JavaScript. I'm encountering an issue where my image will not draw to the canvas, despite being loaded correctly. Here's the code:
//===================================
// class - Canvas
//===================================
var classCanvas = function(id){
canvas = document.getElementById(id);
context = canvas.getContext("2d");
}
//===================================
// abstract class - Image Drawer
//===================================
var classImageDrawer = function(that){
this.draw = function(){
context.drawImage(that.img,
that.spriteCoordsX,
that.spriteCoordsY,
that.width,
that.height,
that.posX,
that.posY,
that.width,
that.height);
}
}
//===================================
// class - Level
//===================================
var classLevel = function(name, src){
this.name = name;
this.img = new Image();
this.img.src = src;
this.img.onload = function(){ };
this.spriteCoordsX = 0;
this.spriteCoordsY = 0;
this.posY = 0;
this.posX = 0;
this.height = 400;
this.width = 600;
this.drawer = new classImageDrawer(this);
}
//==================================
// Begin
//==================================
var game = new classCanvas("playground");
game.LevelOne = new classLevel("LevelOne", "images/foreground-land.png");
game.LevelOne.drawer.draw();
I have checked the code, and I know the image is getting loaded correctly. No errors occur during runtime, the image "foreground-land.png" simply DOESN'T show up on the canvas!
Your image may be loading fine, but as far as I can tell your call to game.LevelOne.drawer.draw() has no guarantee that foreground-land.png has loaded at that point (remember, images are loaded asynchronously without blocking the execution of other code).
Your onload function for the image object in your classLevel class is empty and anonymous. Nothing happens when the image is finished loading. I'm not sure how you want to structure your code given everything else you have going on, but you should only allow the call to game.LevelOne.drawer.draw() to succeed if the resources it depends upon have been fully loaded. The best way to do this would be to hook into the onload callback for all resources you require for that object (in this case, it's just the one image).
Loading images is an async operation, you are (basically) ignoring the loading process, and acting as though it is sync operations.
One thing you could look at is using a 3rd parameter as a callback ("LevelOne", "images/foreground-land.png", function() { this.drawer.draw() }) and doing that as the onload

Categories

Resources