KineticJS: no method '_addId' in anonymous function - ruby-on-rails

So, trying to work through the KineticJS.Image tutorial, but I run into an issue.
This works:
$(document).ready( function() {
var icon = new Image();
icon.onload = function() {
var stage = new Kinetic.Stage({
container: 'canvas',
width: 730,
height: 700,
});
var layer = new Kinetic.Layer();
var im = new Kinetic.Image({
x: -14,
y: -13,
image: icon,
width: 30,
height: 30
});
layer.add(im);
stage.add(layer);
};
icon.src = '#{asset_path "icons.png"}';
});
this, which very nearly the tutorial code, does not:
$(document).ready( function() {
var stage = new Kinetic.Stage({
container: 'targetCanvas',
width: 730,
height: 700,
});
var layer = new Kinetic.Layer();
var icon = new Image();
icon.onload = function() {
var im = new Kinetic.Image({
image: icon,
width: 30,
height: 30,
});
layer.add(im);
stage.add(layer);
};
icon.src = '#{asset_path "designer_icons.png"}';
});
It throws:
Uncaught TypeError: Object #<Object> has no method '_addId' kinetic.js:28
Kinetic.Container.add
Kinetic.Stage.add
icon.onload
I could have something Seriously Messed up in my environment, since this is in a serious hack job of a project (Rails w/Backbone,Require,Bootstrap and Kinetic), and I really don't know javascript or various libraries, so I'm fumbling around in the dark.
Any ideas what's causing this and how to fix it?

It seems stage is not initialized as a Kinetic.Node properly. Could you make it sure this exists?
<div id="targetCanvas"></div>
and also, please make it sure you are using version 4.3.1?
I don't see 4.3.1 has that line.
If the above does not help, please post your example, so that I can take a look at it.

Related

What is the proper way how to fire event on start and finish of the complex drawing using KanvaJS?

I am drawing a complex graphic on canvas using KonvaJS library. Execution takes few seconds and I want to monitor progress on user screen, but I can't make work functions like:
layer.on('draw', () => {
console.log('Draw finished')
})
What is the proper way how to fire event on start and finish of the drawing using KanvaJS?
Here is a test code where console.log('Draw finished') is not executed
var stage = new Konva.Stage({
container: 'canvas-container', // id of HTML div container
width: 1000,
height: 500,
});
function drawGrid(){
let layerMPE = new Konva.Layer();
let pxMax = stage.width();
let pyMax = stage.height();
let res = 10;
for (let px=0; px < pxMax; px += res){
for (let py=0; py < pyMax; py += res){
let rect = new Konva.Rect({
x: px,
y: py,
width: res,
height: res,
fill: 'red'
});
layerMPE.add(rect);
}
}
stage.add(layerMPE);
layerMPE.draw();
layerMPE.on('draw', () => {
console.log('Draw finished')
});
}
drawGrid();

Konva converting stage toDataUrl() do not work in chrome browser,but work well in safari browser

I have Konva Stage with few layers, when I try convert to image all stage - result is OK in safari browser , when I try convert stage in chrome ,firefox etc. - result is failed. I think that toDataUrl() method does
not work well in chrome browser ,firefox browser etc except for safari browser
<button id="save" type="button">save</button>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;
function downloadURI(uri, name) {
var link = document.createElement('a');
link.download = name;
link.href = uri;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
delete link;
}
function drawImage(imageObj) {
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var layer = new Konva.Layer();
// darth vader
var darthVaderImg = new Konva.Image({
image: imageObj,
x: stage.getWidth() / 2 - 200 / 2,
y: stage.getHeight() / 2 - 137 / 2,
width: 200,
height: 137,
name: 'myimg',
draggable: true
});
// add cursor styling
darthVaderImg.on('mouseover', function () {
document.body.style.cursor = 'pointer';
});
darthVaderImg.on('mouseout', function () {
document.body.style.cursor = 'default';
});
layer.add(darthVaderImg);
stage.add(layer);
stage.on('click tap', function (e) {
// if click on empty area - remove all transformers
if (e.target === stage) {
stage.find('Transformer').destroy();
layer.draw();
return;
}
// do nothing if clicked NOT on our rectangles
if (!e.target.hasName('myimg')) {
return;
}
// remove old transformers
// TODO: we can skip it if current rect is already selected
stage.find('Transformer').destroy();
// create new transformer
var tr = new Konva.Transformer();
layer.add(tr);
tr.attachTo(e.target);
layer.draw();
})
document.getElementById('save').addEventListener(
'click',
function () {
var dataURL = stage.toDataURL({ pixelRatio: 3 });
downloadURI(dataURL, 'stage.png');
},
false
);
}
var imageObj = new Image();
imageObj.onload = function () {
drawImage(this);
};
imageObj.src = 'https://www.decanterchina.com/assets/images/article/550/136031_decanter-cava-tasting-1.jpg';
</script>
Any possible reasons or solutions ? Thanks!!
If you look into the console you will see a message:
Konva warning: Unable to get data URL. Failed to execute 'toDataURL'
on 'HTMLCanvasElement': Tainted canvases may not be exported.
You have CORS issue. Take a look here for solutions:
Tainted canvases may not be exported
https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image

Why is the zIndex sequence of my objects not what I expected?

How can I get a list of all objects with all params (x, y, width, etc.), including zIndex param on the stage after completing resizing? And how can I set an zIndex for each object when creating a stage?
I have this code, but setZIndex not working correctly. Images are not set correctly.
const oKonvaStage = new Konva.Stage({
container: 'dropzone'
});
const oKonvaLayer = new Konva.Layer();
oKonvaStage.add(oKonvaLayer);
const oKonvaImage1 = new Konva.Image({
x: 624,
y: 433,
width: 1920,
height: 1280
});
const oImage1 = new Image();
oImage1.onload = function() {
oKonvaImage1.image(oImage1);
oKonvaLayer.add(oKonvaImage1);
oKonvaImage1.setZIndex(2);
oKonvaLayer.draw();
};
oImage1.src = 'image1.jpg';
oKonvaImage1.on('transformend', function(e) {
UpdateAttrs();
});
const oKonvaImage2 = new Konva.Image({
x: 9,
y: 254,
width: 1024,
height: 1024
});
const oImage2 = new Image();
oImage2.onload = function() {
oKonvaImage2.image(oImage2);
oKonvaLayer.add(oKonvaImage2);
oKonvaImage2.setZIndex(0);
oKonvaLayer.draw();
};
oImage2.src = 'image2.jpg';
oKonvaImage2.on('transformend', function(e) {
UpdateAttrs();
});
const oKonvaImage3 = new Konva.Image({
x: -586,
y: -315,
width: 1920,
height: 1199
});
const oImage3 = new Image();
oImage3.onload = function() {
oKonvaImage3.image(oImage3);
oKonvaLayer.add(oKonvaImage3);
oKonvaImage3.setZIndex(1);
oKonvaLayer.draw();
};
oImage3.src = 'image3.jpg';
Image3 has index = 1 but is over Image2 which has index = 2.
First off, prompted by #lavrton's comment, you should add the konva.Images to the canvas as soon as you have instantiated them - not in the image onload event. The image objects are no overhead to the canvas, and you can then be sure of the initial z-index sequence. You may change the sequence after that, but at least you start with a known layout.
And as a general rule, you need to take care when using any commands inside the onload event of an image. Image loading is asynchronous - meaning it does not happen in the sequence you might anticipate when you write the code. A large image coming from a slow server will load more slowly than a small image from a quick server, but you cannot make any assumptions about the sequence. The ONLY way you can ensure the sequence is to have the load of the second image initiated from the onload event of the first, but this is generally going to give bad UX.
Back to the code you posted, the code in my snippet below would appear to work as you intended. I switched the ECMA2015 const to plain old vars, and removed the unnecessary on-transforms.
I also added some code to analyse the results, showing the hoped-for zIndex value and the achieved zIndex values.
Note that the zIndex value in Konva is relative to the parent container and not absolute.
So, for example, when I set zondex=999 I actually get a value of 4.
Summary:
avoid calling code for which sequence is critical in onload events.
do not expect to get exactly the zindex you ask for.
var div = $('#dropzone');
var oKonvaStage = new Konva.Stage({container: 'dropzone', width: div.width(), height: div.height()});
var indexWanted = [];
var oKonvaLayer = new Konva.Layer();
oKonvaStage.add(oKonvaLayer);
var oKonvaImage1 = new Konva.Image({
name: 'image-1',
x: 20,
y: 20,
width: 300,
height: 100
});
var oImage1 = new Image();
oImage1.onload = function() {
oKonvaLayer.add(oKonvaImage1);
oKonvaImage1.image(oImage1);
oKonvaImage1.setZIndex(2);
indexWanted[0] = 2;
oKonvaLayer.draw();
sayPos();
};
oImage1.src = 'https://dummyimage.com/300x100/666/fff.png?text=Image-1'
var oKonvaImage2 = new Konva.Image({
name: 'image-2',
x: 10,
y: 100,
width: 300,
height: 100
});
var oImage2 = new Image();
oImage2.onload = function() {
oKonvaImage2.image(oImage2);
oKonvaLayer.add(oKonvaImage2);
oKonvaImage2.setZIndex(0);
indexWanted[1] = 0;
oKonvaLayer.draw();
sayPos();
};
oImage2.src = 'https://dummyimage.com/300x100/333/fff.png?text=Image-2';
var oKonvaImage3 = new Konva.Image({
name: 'image-3',
x: 280,
y: 80,
width: 300,
height: 100
});
var oImage3 = new Image();
oImage3.onload = function() {
oKonvaImage3.image(oImage3);
oKonvaLayer.add(oKonvaImage3);
oKonvaImage3.setZIndex(999); // <<<< notice this is set to 99. Compare to console output !!
indexWanted[2] = 999;
oKonvaLayer.draw();
sayPos();
};
oImage3.src = 'https://dummyimage.com/300x100/ccc/fff.png?text=Image-3';
oKonvaLayer.draw();
oKonvaStage.draw();
var picCnt = 0, s= '', imgNo = 0;
function sayPos(){
picCnt = picCnt + 1;
if (picCnt === 3){
for (var i = 0; i < indexWanted.length; i = i + 1){
imgNo = i + 1;
s = s + '<br/>Image-' + imgNo + ' zindex wanted = ' + indexWanted[i] + ' actual zIndex=' + oKonvaLayer.findOne('.image-' + imgNo).getAbsoluteZIndex();
}
$('#info').html(s)
}
}
#info {
font-size: 10pt;
height: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.min.js"></script>
<p id='info' >
</p>
<div id='dropzone' style="position: absolute; top: 90px; z-index: -1; display: inline-block; left: 0px; width: 600px; height: 400px; background-color: silver;"></div>

update position of geolocation marker in Openlayers 3

I wan't to have a marker on the map, which position is dynamically updated.
When I create the layer inside the function of the geolocation.on('change'-event it works, but the layer is added each time the geolocation changes. Therefore I wanted to create the layer outside that function and update only the position of the marker.
With the folowing code I get an 'TypeError: a is null'
var geolocation = new ol.Geolocation({
projection: map.getView().getProjection(),
tracking: true,
trackingOptions: {
enableHighAccuracy: true,
maximumAge: 2000
}
});
var iconStyle = new ol.style.Style({
image: new ol.style.Icon({
anchor: [0.5, 46],
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
opacity: 0.75,
src: './_img/marker_.png'
})
});
var pos1 = geolocation.getPosition();
var iconFeature = new ol.Feature({
geometry: new ol.geom.Point(pos1)
});
var iconSource = new ol.source.Vector({
features: [iconFeature]
});
var iconLayer = new ol.layer.Vector({
source: iconSource,
style : iconStyle
});
map.addLayer(iconLayer);
geolocation.on('change', function() {
var pos_upd = geolocation.getPosition();
iconFeature.getGeometry().setCoordinates(pos_upd);
view.setCenter(pos_upd);
view.setZoom(18);
});
The browser Geolocation API, which is wrapped by ol.Geolocation, is asynchronous. After initiation, geolocation.getPosition() will always return undefined until the first change event.
The proper thing to do is to add the feature once you get a coordinate, in the change event handler.
You will need to use a conditional to determine if you should add ore update a feature.
I changed the code a bit, so that I created first an iconFeature, that wasn't already bound to a ol.geom.Point()-position. By this way there was no need to use geolocation.getPosition().
Later in the geolocation.on('change')-event I assigned the actual position to the geometry of the iconFeature.
Works like espected
// add an empty iconFeature to the source of the layer
var iconFeature = new ol.Feature();
var iconSource = new ol.source.Vector({
features: [iconFeature]
});
var iconLayer = new ol.layer.Vector({
source: iconSource,
style : iconStyle
});
map.addLayer(iconLayer);
// Update the position of the marker dynamically and zoom to position
geolocation.on('change', function() {
var pos = geolocation.getPosition();
iconFeature.setGeometry(new ol.geom.Point(pos));
view.setCenter(pos);
view.setZoom(18);
});

cannot add features to WFS layer

I try to load some features from Geoserver to a vector layer in Openlayers 3.9.0.
var url = 'http://localhost:5550/geoserver/mymap/wfs?service=WFS&'+'version=1.0.0&request=GetFeature&typeName=mymap:layer&'+'outputFormat=application/json&maxFeatures=50';
var projection = ol.proj.get('EPSG:3857');
var extent = [2297128.5, 4618333, 2459120.25, 4763120];
var amir = new ol.source.Vector({
format: new ol.format.GeoJSON(),
loader: function (extent) {
$.ajax(url, {type: 'GET'})
.done(loadFeatures)
.fail(function () {alert("error");});
},
strategy: ol.loadingstrategy.bbox
});
function loadFeatures(response) {
formatWFS = new ol.format.WFS();
var features = formatWFS.readFeatures(response);
amir.addFeatures(features);
//-----------OR---------------------
var features = amir.readFeatures(response);
amir.addFeatures(features);
}
var fill = new ol.style.Fill({
color: 'rgba(0,0,0,0.2)'
});
var stroke = new ol.style.Stroke({
color: 'rgba(0,0,0,0.4)'
});
var circle = new ol.style.Circle({
radius: 6,
fill: fill,
stroke: stroke
});
jake = new ol.layer.Vector({
source: amir,
style: new ol.style.Style({
fill: fill,
stroke: stroke,
image: circle
})
});
In loadFeatures function if I use
formatWFS = new ol.format.WFS();
var features = formatWFS.readFeatures(response);
amir.addFeatures(features);
I get Uncaught AssertionError: Failure: Unknown source type pointing to a openlayers line that throws errors and to this line of my codevar features = formatWFS.readFeatures(response);.
If I use
var features = amir.readFeatures(response);
amir.addFeatures(features);
I get Uncaught TypeError: sourceVector.readFeatures is not a function pointing to var features = amir.readFeatures(response); .
The request to the WFS looks ok, with OK 200 status. If I grab the request's URL sended to Geoserver and open it in a new tab I get raw GeoJSON like {"type":"FeatureCollection","totalFeatures":422,"features":[{"type":"Feature","id":"layer.709","geometry":{"type":"Point","coordinates":[2391735.8907621,4695330.8039257005]},"geometry_name":"l_geom","properties":{"l_name":"Leeron"}},....//next feature
So its a FeatureCollection not just an array. Not that I know how to handle this
I dont get why to set a ol.format.WFS and not just read/add features. I dont know how to debug and add the features to my layer
You are instructing GeoServer to use GeoJSON as output format, so you'll need to use the GeoJSON format in OpenLayers to parse the features. You should be able to simplify your source configuration to something like
var url = 'http://localhost:5550/geoserver/mymap/wfs?service=WFS&' +
'version=1.0.0&request=GetFeature&typeName=mymap:layer&' +
'outputFormat=application/json&maxFeatures=50';
var amir = new ol.source.Vector({
format: new ol.format.GeoJSON(),
url: function(extent, projection) {
return url + '&bbox=' + extent.join(',') +
'&srsName=' + projection.getCode();
},
strategy: ol.loadingstrategy.bbox
});

Resources