Add more images on Canvas with Kinetic - javascript

Im making an app, but I ve met the problem. I need when I hit a button add new point (image) on my Canvas. Here is a code:
var ID = 0;
var points = [];
function addPoint(){
points.push({id: ID, posX: 0, posY: 0, url: "img/point.png"});
ID++;
showPoints();
}
function showPoints(){
var img = new Array();
var point = new Array();
var stage = new Kinetic.Stage({
container: 'cvsCroatia',
width: 574,
height: 508
});
var layer = new Kinetic.Layer();
for(var j=0; j < ID; j++){
img[j] = new Image();
img[j].src = 'img/point.png';
img[j].onload = (function(){
point[j] = new Kinetic.Image({
x: points[j].posX,
y: points[j].posY,
image: img[j],
width: 13,
height: 13,
name: img[j],
draggable: true
});
});
layer.add(point[j]);
}
stage.add(layer);
}
But i got an error:
Uncaught TypeError: Cannot set property 'index' of undefined (kinetic-v4.4.0.min.js:29)
Have you got any idea, what is wrong? Thx for answers. Alan..

var img = new Array(); // the simplest solution is to make img visible outside your function
function showPoints(){
// var img = new Array(); // HERE img is a LOCAL variable, only visible to the showPoints() function
var point = new Array();
var stage = new Kinetic.Stage({
container: 'cvsCroatia',
width: 574,
height: 508
});

Thanks for answer, i put all variables to the global scope, but unfortunately got the same error..
var ID = 0;
var points = [];
var img = new Array();
var point = new Array();
function showPoints(){
var stage = new Kinetic.Stage({
container: 'cvsCroatia',
width: 574,
height: 508
});
var layer = new Kinetic.Layer();
for(var j=0; j < ID; j++){
img[j] = new Image();
img[j].src = 'img/point.png';
img[j].onload = (function(){
point[j] = new Kinetic.Image({
x: points[j].posX,
y: points[j].posY,
image: img[j],
width: 13,
height: 13,
name: img[j],
draggable: true
});
});
layer.add(point[j]);
}
stage.add(layer);
}
function addPoint(){
points.push({id: ID, posX: 0, posY: 0, url: "img/point.png"});
ID++;
showPoints();
}

Related

Why I cannot draw multiple curved arrows with sceneFunc() in Shape object using KonvaJS?

I am inspired with KonvaJS tutorial Modify Curves with Anchor Points to make my own example which is to create multiple custom arrows.
on click on the selectionBox create an anchor.
on the creation of the third anchor create the curved arrow.
on the fourth click reset all variables in order to draw a new curved arrow.
var width = window.innerWidth;
var height = window.innerHeight;
// globals
var selectionBoxLayer, curveLayer, lineLayer, anchorLayer, quad, bezier;
function updateDottedLines() {
var q = quad;
var quadLine = lineLayer.get('#quadLine')[0];
quadLine.setPoints([q.start.attrs.x, q.start.attrs.y, q.control.attrs.x, q.control.attrs.y, q.end.attrs.x, q.end.attrs.y]);
lineLayer.draw();
}
function buildAnchor(x, y) {
var anchor = new Konva.Circle({
x: x,
y: y,
radius: 20,
stroke: '#666',
fill: '#ddd',
strokeWidth: 2,
draggable: true
});
// add hover styling
anchor.on('mouseover', function() {
document.body.style.cursor = 'pointer';
this.setStrokeWidth(4);
anchorLayer.draw();
});
anchor.on('mouseout', function() {
document.body.style.cursor = 'default';
this.setStrokeWidth(2);
anchorLayer.draw();
});
anchor.on('dragend', function() {
drawCurves();
updateDottedLines();
});
anchorLayer.add(anchor);
anchorLayer.draw();
return anchor;
}
function drawCurves() {
var context = curveLayer.getContext();
var arrowLine = new Konva.Shape({
sceneFunc: function(context){
debugger;
// draw quad
context.beginPath();
context.moveTo(quad.start.attrs.x, quad.start.attrs.y);
context.quadraticCurveTo(quad.control.attrs.x, quad.control.attrs.y, quad.end.attrs.x, quad.end.attrs.y);
//Draw Arrow Head
var headlen = 10; // length of head in pixels
var angle = Math.atan2(quad.end.attrs.y - quad.control.attrs.y, quad.end.attrs.x - quad.control.attrs.x);
context.lineTo(quad.end.attrs.x-headlen*Math.cos(angle-Math.PI/6), quad.end.attrs.y-headlen*Math.sin(angle-Math.PI/6));
context.moveTo(quad.end.attrs.x, quad.end.attrs.y);
context.lineTo(quad.end.attrs.x- headlen*Math.cos(angle+Math.PI/6), quad.end.attrs.y-headlen*Math.sin(angle+Math.PI/6));
context.fillStrokeShape(this);
},
stroke: 'black',
strokeWidth: 4
});
curveLayer.add(arrowLine);
curveLayer.draw();
}
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
selectionBoxLayer = new Konva.Layer();
anchorLayer = new Konva.Layer();
lineLayer = new Konva.Layer();
// curveLayer just contains a canvas which is drawn
// onto with the existing canvas API
curveLayer = new Konva.Layer();
var quadLine = new Konva.Line({
dash: [10, 10, 0, 10],
strokeWidth: 3,
stroke: 'black',
lineCap: 'round',
id: 'quadLine',
opacity: 0.3,
points: [0, 0]
});
// add dotted line connectors
lineLayer.add(quadLine);
quad = {};
// keep curves insync with the lines
anchorLayer.on('beforeDraw', function() {
if(quad.start && quad.control && quad.end){
drawCurves();
updateDottedLines();
}
});
var selectionBoxBackground = new Konva.Rect({
x: 0,
y: 0,
height:stage.height(),
width: stage.width(),
fill: 'transparent',
draggable: false,
name: 'selectionBoxBackground'
});
selectionBoxLayer.add(selectionBoxBackground);
var clickCounter = 0;
selectionBoxBackground.on("click", function(){
clickCounter +=1;
var mousePos = {};
switch(clickCounter){
case 1:
mousePos = stage.getPointerPosition();
quad.start = buildAnchor(mousePos.x, mousePos.y);
break;
case 2:
mousePos = stage.getPointerPosition();
quad.control = buildAnchor(mousePos.x, mousePos.y);
break;
case 3:
mousePos = stage.getPointerPosition();
quad.end = buildAnchor(mousePos.x, mousePos.y);
drawCurves();
updateDottedLines();
break;
default:
clickCounter = 0;
quad = {};
anchorLayer.destroyChildren();
anchorLayer.draw();
}
});
stage.add(curveLayer);
stage.add(lineLayer);
stage.add(selectionBoxLayer);
stage.add(anchorLayer);
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #F0F0F0;
}
<script src="https://cdn.rawgit.com/konvajs/konva/0.11.1/konva.min.js"></script>
<body>
<div id="container"></div>
</body>
P.S Please note when I write in the browser console curveLayer.children, it will bring all created curved arrows.
Hint: I think on the creation of new Shape() the values of all created shapes will be changed to the new one.
I don't know what I am missing.

kinetic js textPath: draggable didn't work

var stages = new Array() ;
var limites = new Array() ;
numStage=0;
r = {
'x':65,
'y':120,
'xwidth':335,
'yheight':210
};
limites.push(r);
stages[numStage] = new Kinetic.Stage({
container: 'cmg_canvas_'+numStage,
width: 450,
height: 450
});
//creation image
obj = new Image();
obj.src = 'http://i.imgur.com/zFZgKuS.jpg';
image = new Kinetic.Image({
image: obj,
width: 450,
height: 450
});
// add image to calque
layer = new Kinetic.Layer();
layer.add(image);
stages[numStage].add(layer); //add image to canvas
layer = new Kinetic.Layer();
//set limit x y h l
/*var rect = new Kinetic.Rect({
name: 'limite',
x: limites[numStage].x,
y: limites[numStage].y,
width: limites[numStage].xwidth,
height: limites[numStage].yheight,
stroke: 'black',
strokeWidth: 0.5
});*/
//layer.add(rect);// add to canvas
stages[numStage].add(layer);
$('.cmg_text').live('blur', function(){
idText = 'cmg_line0';
numStage = 0;
drawTextPath(numStage, idText,$(this).val(),50,22,numStage);
//text = getText(this).text;
});
function getSVG(x,y,w,verif) {
halfw = parseFloat((w/2).toFixed(2));
x1 = parseFloat((halfw/2).toFixed(2));
x2 = parseFloat(halfw + x1);
if(parseInt(verif))
{
y1 = parseFloat(y) * 2 +18;
y2 = parseFloat(y) * 2 +18;
}
else
{
y1 = -18;
y2 = -18;
}
str = 'M '+x+','+y+' C '+x1+','+y1+' '+x2+','+y2+' '+w+','+y;
return str;
}
function drawTextPath(numStage, textId,text,valueSlider, newFontSize,numStage){
//'M 0,115 C42,-18 126,-18 165,115';
//'M 0,115 C45,230 180,230 180,115';
var arcOnly = 0;
if(textId == 'cmg_line0')
{
console.log('limites[numStage].yheight/2'+limites[numStage].yheight/2);
console.log('limites[numStage].xwidth'+limites[numStage].xwidth);
svg = getSVG(0,valueSlider,valueSlider*6.3,0);
arcOnly = 0;
}
//alert(svg);
console.log(parseFloat(limites[numStage].y));
console.log(parseFloat(arcOnly));
console.log(parseFloat(limites[numStage].y - arcOnly));
var layer = new Kinetic.Layer({name:'textPathLayer',draggable:true});
var textpath = new Kinetic.TextPath({
name:'TextPath',
id: textId,
//x: 0,
//x: limites[numStage].x + limites[numStage].xwidth/2,
//y: 0,
//y: limites[numStage].y + limites[numStage].yheight/2,
x: limites[numStage].x ,
y: limites[numStage].y + limites[numStage].yheight/2,
fill: '#000',
fontSize: newFontSize,
fontFamily: 'Arial',
text: text,
//offsetX:0,
//offsetY:0,
draggable: true,
dragBoundFunc: function(pos){
p = textParams(this, pos);
return {x: p.newX, y: p.newY};
},
data: svg
});
//
layer.add(textpath);
stages[numStage].add(layer);
//layer.moveToTop();
//layer.draw();
//stages[0].draw();
}
<script src="http://cdnjs.cloudflare.com/ajax/libs/kineticjs/4.6.0/kinetic.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<div id="cmg_canvas_0"></div>
<input type='text' class='cmg_text' />
I have to draw a draggable textpath with kineticjs, with text given by an input text, the action is triggered in blur.
I have a stage that contain 3 layers;
Layer for the background, and one layer for the textpath.
My problem that the draggable in the textpath is not working,
i tried to set the text layer in the top, but i didn't get it draggable.
This is my jsfiddle
I have a doubt of inner layer problem.
Thanks in advance.

Collision detection using kineticJS (getIntersection function not working)

I am trying to recreate the game http://www.sinuousgame.com/ and started studying html5 canvas and kineticJS.
Recently i came across the getIntersection function and coudnt find much details regarding it.But with what i had ,i did make a code to get the Collision detection done using getIntersection() function.
But it doesnt seem to be working.
As you can see, My Fiddle: http://jsfiddle.net/p9fnq/8/
//The working player code
var LimitedArray = function(upperLimit) {
var storage = [];
// default limit on length if none/invalid supplied;
upperLimit = +upperLimit > 0 ? upperLimit : 100;
this.push = function(item) {
storage.push(item);
if (storage.length > upperLimit) {
storage.shift();
}
return storage.length;
};
this.get = function(flag) {
return storage[flag];
};
this.iterateItems = function(iterator) {
var flag, l = storage.length;
if (typeof iterator !== 'function') {
return;
}
for (flag = 0; flag < l; flag++) {
iterator(storage[flag]);
}
};
};
var tail = new LimitedArray(50);
var flag = 0, jincr = 0;
var stage = new Kinetic.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
listening: true
});
var layer = new Kinetic.Layer({
listening: true
});
stage.add(layer);
var player = new Kinetic.Circle({
x: 20,
y: 20,
radius: 6,
fill: 'cyan',
stroke: 'black',
draggable: true
});
var line = new Kinetic.Line({
points: [],
stroke: 'cyan',
strokeWidth: 2,
lineCap: 'round',
lineJoin: 'round'
});
layer.add(line);
layer.add(player);
// move the circle with the mouse
stage.getContent().addEventListener('mousemove', function() {
player.position(stage.getPointerPosition());
var obj = {
x: stage.getPointerPosition().x,
y: stage.getPointerPosition().y
};
tail.push(obj);
var arr = [];
tail.iterateItems(function(p) {
arr.push(p.x, p.y);
});
line.points(arr);
});
var x = 0;
var y = 0;
var noOfEnemies = 200;
var enemyArmada = new Array();
createEnemy();
function createEnemy() {
for (var i = 0; i < noOfEnemies; i++) {
var enemy = new Kinetic.Circle({
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
radius: 4.5 + 1.5 * Math.random(),
fill: 'red',
stroke: 'black'
});
enemy.speedX = enemy.speedY = (0.5 + Math.random() * 50);
enemyArmada.push(enemy);
layer.add(enemy);
}
}
var checkCollide = function() {
var position = stage.getPointerPosition();
if(position == null)
position = player.position();
if(position == null)
position = {x:0,y:0};
var collided = stage.getIntersection(position);
console.log(position);
if (typeof collided !== 'Kinetic.Shape') {
console.log("not shape");
}
else {
console.log("BOOOM!!!");
}
};
var anim = new Kinetic.Animation(function(frame) {
checkCollide();
for (var i = 0; i < noOfEnemies; i++) {
var e = enemyArmada[i];
e.position({
x: e.position().x - e.speedX * (frame.timeDiff / 400),
y: e.position().y + e.speedY * (frame.timeDiff / 400)
});
if (e.position().y < 0 || e.position().x < 0) {
e.position({
x: (Math.random() * (window.innerWidth + 600)),
y: -(Math.random() * window.innerHeight)
});
}
}
}, layer);
anim.start();
I need the collision to be detected. The function i have written here is checkCollide and its called within the kinetic.Animation function.
Can anyone help me out with this??
(If you don't know the solution,please do like the post,i need the solution badly)
The source of the problem
getIntersection(point) means "is any object at this point".
Since the point you're using is the player's position, getIntersection will always return true because player is always at its own position !
One solution
Put your player on one layer and all enemies on a separate layer.
That way you can hit test the enemy layer without the interference of the player object.
Code and a Demo: http://jsfiddle.net/m1erickson/JCfW8/
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Prototype</title>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v5.0.1.min.js"></script>
<style>
body{padding:20px;}
#container{
border:solid 1px #ccc;
margin-top: 10px;
width:350px;
height:350px;
}
</style>
<script>
$(function(){
var stage = new Kinetic.Stage({
container: 'container',
width: 350,
height: 350
});
var enemyLayer = new Kinetic.Layer();
stage.add(enemyLayer);
var playerLayer = new Kinetic.Layer();
stage.add(playerLayer);
var player = new Kinetic.Circle({
x:100,
y:100,
radius: 10,
fill: 'green',
draggable: true
});
player.on("dragmove",function(){
if(enemyLayer.getIntersection(player.position())){
this.fill("red");
playerLayer.draw();
}
});
playerLayer.add(player);
playerLayer.draw();
var enemy = new Kinetic.Circle({
x:200,
y:100,
radius: 20,
fill: 'blue',
draggable: true
});
enemyLayer.add(enemy);
enemyLayer.draw();
}); // end $(function(){});
</script>
</head>
<body>
<h4>Drag the green player<br>Player will turn red if it collides<br>with the blue enemy</h4>
<div id="container"></div>
</body>
</html>
Another solution
Mathematically test the player against every enemy:
Warning: untested code--some tweaking might be required
function playerEnemyCollide(){
var playerX=player.x();
var playerY=player.y();
var playerRadius=player.radius();
for(var i=0;i<enemyArmada.length;i++){
var e=enemyArmada[i];
if(circlesColliding(playerX,playerY,playerRadius,e.x,e.y,e.radius)){
return(true);
}
}
return(false);
}
function circlesColliding(cx1,cy1,radius1,cx2,cy2,radius2){
var dx=cx2-cx1;
var dy=cy2-cy1;
return(dx*dx+dy*dy<(radius1*2+radius2*2);
}

unable to load multiple images in a canvas using a loop

I am trying to load multiple images in a loop but no idea why it ain't working.
LoadCharactersImages2:function(Layer,roomObject)
{
var imgs = Array(5);
var ImgBanners = Array(5);
var sources = Array(5);
var locationX = 100; //x coordinate location of image
var self = this;
self.ImgloadCount = 0;
sources[0] = "images/pacman/pacman_right.png";
sources[1] = "images/pacman/pinkdown.png";
sources[2] = "images/pacman/redleft.png";
sources[3] = "images/pacman/yellowup.png";
sources[4] = "images/pacman/blueright.png";
for(var i = 0 ; i < 5;i++)
{
imgs[i] = new Image();
imgs[i].src = sources[i];
imgs[i].onload = function() {
ImgBanners[i] = new Kinetic.Image({
x: locationX,
y: 100,
image: imgs[i],
});
Layer.add(ImgBanners[i]);
self.ImgloadCount++;
self.AddtoStage(Layer,self);
self.LoadUsersImages(self,roomObject,Layer);
}
locationX = locationX + 100;
}
},
AddtoStage:function(layer,self)
{
if(self.ImgloadCount == 5)
self.stage.add(layer);
},
AddtoStage will add layer to stage when all the images are loaded. Now why it is not showing images on the canvas whats the problem ?
EDIT:
Now i edited the code to work it with closures as #zeta suggested but still no success.Here's the edited code.
imgs[i].onload = (function(index){
return function() {
ImgBanners[index] = new Kinetic.Image({
x: locationX,
y: 100,
image: imgs[index],
});
Layer.add(ImgBanners[index]);
self.ImgloadCount++;
self.AddtoStage(Layer,self);
self.LoadUsersImages(self,roomObject,Layer);
Layer.draw();
}
})(i);
You need a closure:
imgs[i].onload = (function(index){
return function() {
ImgBanners[index] = new Kinetic.Image({
x: locationX,
y: 100,
image: imgs[index],
});
})(i);
See also:
How do JavaScript closures work?
Please have a look at that link. Loads images in a loop of coffeescript
https://gist.github.com/4589644
I solved the problem by removing return function from the statement.

using canvas kinetic in html5 to draw many text and edit them

When I click the text on a card, I want to be able to edit the text like this website:
http://www.vistaprint.com/vp/ns/studio3.aspx?pf_id=064&combo_id=120585&free_studio_gallery=true&referer=http%3a%2f%2fwww.vistaprint.com%2fvp%2fns%2fdefault.aspx%3fdr%3d1%26rd%3d1%26GNF%3d0%26GP%3d5%252f19%252f2012%2b12%253a36%253a37%2bAM%26GPS%3d2448654652%26GNF%3d1%26GPLSID%3d&rd=1
Here is my code:
$(document).ready(function () {
var Total_layers = 0;
var Text = {};
/*set up stage for drawing image*/
var stage = new Kinetic.Stage({
container: "container",
width: 600,
height: 400
});
// var Layer = {};
/*create a layer object for placing text image over it and place it over the stage*/
var layer = new Kinetic.Layer();
//Layer[Total_layers]
/*Text Property*/
var New_Text = "Company Name";
var Text_Font = "Arial";
var Text_Color = "Black";
var Text_Size = "30";
var Text_Pos_X = 200;
var Text_Pos_y = 100;
var Selected_Text = new Kinetic.Text({});
var current_layer = 0;
// var text_selected = 1;
/*Add event for them*/
//var formElement = document.getElementById("New Text");
// formElement.addEventListener('change', Text_Changed, false);
var formElement = document.getElementById("selectFontSize");
formElement.addEventListener('change', Text_Size_Changed, false);
/*This Function will be Executed when the Size of the Text in consideration is changed*/
function Text_Size_Changed(e) {
var target = e.target;
Text_Size = target.value;
Text_Pos_X = 200; //Text[Total_layers].x;
Text_Pos_Y = 100; //Text[Total_layers].y;
//DeleteLayer(Total_layers);
layer.remove(Selected_Text);
Draw_text(Total_layers);
}
/*Function to swap the Kinetic Text object and get the selected Text object to The Top*/
function swap_layers(Selected_text) {
var temp = new Kinetic.Text({});
for (var i = 1; i <= Total_layers; i++) {
if (Text[i] == Selected_text) {
temp = Text[i];
Text[i] = Text[Total_layers];
Text[Total_layers] = temp;
break;
}
}
}
/*Add different Events to the Text objects once They are instantiated*/
function add_events(dest_Text) {
dest_Text.on("mouseover", function () {
document.body.style.cursor = "pointer";
});
dest_Text.on("mouseout", function () {
document.body.style.cursor = "default";
});
dest_Text.on("click", function () {
$("#selectFontSize").change(function () {
dest_Text.setFontSize($("#selectFontSize").val());
layer.draw(); // vì gọi layer.draw nên tất cả text trong layer đó đều dc vẽ lại
});
$("#selectFontFamily").change(function () {
swap_layers(dest_Text);
//dest_Text.setFontFamily($("#selectFontFamily").val());
//layer.draw();
});
});
}
/*Draw the Text over the layer depening upon the Text_object_Number*/
function Draw_text(Text_object_Number) {
/*Set the Properties of the Topmost object that is been modified and which will be added to the layer*/
Text[Text_object_Number] = new Kinetic.Text({
x: Text_Pos_X,
y: Text_Pos_Y,
text: New_Text,
fontSize: Text_Size,
fontFamily: Text_Font,
textFill: Text_Color,
align: "center",
verticalAlign: "middle",
draggable: "true"
});
/*Adds all the Text objects onto the layer and adds the events to every Text object */
//for (var i = 1; i <= Text_object_Number; i++) {
layer.add(Text[Text_object_Number]);
add_events(Text[Text_object_Number]);
//}
stage.add(layer);
}
$("#add_text").click(function () {
Total_layers++;
Text[Total_layers] = new Kinetic.Text({
x: Text_Pos_X,
y: Text_Pos_y,
text: New_Text,
fontSize: 30,
fontFamily: Text_Font,
textFill: Text_Color,
align: "center",
verticalAlign: "middle",
draggable: "true"
});
add_events(Text[Total_layers]);
layer.add(Text[Total_layers]);
stage.add(layer);
});
/*Adding an image to the present Context*/
var imageObj = new Image();
//alert("abc");
imageObj.onload = function () {
var T_shirt = new Kinetic.Image({
x: 60,
y: 0,
image: imageObj,
width: 550,
height: 400,
name: "image"
});
layer.add(T_shirt);
stage.add(layer);
}
imageObj.src = "../../Content/Image/imagepreview1.jpg";
});
I have tried many ways, but I still can't resolve this problem.
How can I do this by using canvas kineticjs in html5?
The very best solution imho is to use a simple JavaScript prompt:
myText.on('click', function(evt) {
this.setText(prompt('New Text:'));
layer.draw(); //redraw the layer containing the textfield
});
See my answer in this thread: editable Text option in kinetic js
You can't edit the text directly on the canvas, but what you can do is change it with events. So what you need is an input form that is created next to the canvas, and you can read from the form using javascript.
<input type=text id=changeText/>
this way when you click on some text a new input tag will appear and you can type in it and the text inside of the canvas will change as you type.
mytext.on('click', function(){ ... create new input element at the side ... });
//add some jQuery
$('#changeText').onchange( mytext.setText($('$changeText').val()));

Categories