I'm work with kienticJS, I'm trying to customize my own sprite, but I get the next mistake:
Uncaught TypeError: Cannot call method 'getSprite' of undefined escena.js:15
Escena escena.js:15
(anonymous function) aplicacion.js:25
st.event.dispatch jquery.min.js:2
y.handle
So, I've a class Escena when I "run" the game. I've a class Persona that is my sprite, so the code is the next:
Class Escena:
var Escena = function()
{
this.stage = new Kinetic.Stage({
container: 'simulacion',
width: 578,
height: 200
});
this.layer = new Kinetic.Layer();
this.persona = new Persona();
this.layer.add( this.persona.getSprite() ); //IT'S HERE THE MISTAKE
this.stage.add( this.layer );
};
The class Persona is:
var Persona = function()
{
this.ancho= 26;
this.alto = 70;
this.sprite ;
var animaciones = {
caminar:
[ { x: 7, y: 38, width: this.ancho, height: this.alto },
{ x: 37, y: 38, width: this.ancho, height: this.alto },
{ x: 68, y: 38, width: this.ancho, height: this.alto },
{ x: 95, y: 38, width: this.ancho, height: this.alto },
{ x: 127, y: 38, width: this.ancho, height: this.alto },
{ x: 157, y: 38, width: this.ancho, height: this.alto },
{ x: 186, y: 38, width: this.ancho, height: this.alto }
]
};
this.imagen = new Image();
this.imagen.onload = function(){
this.sprite = new Kinetic.Sprite({
x:250,
y:250,
image: this.imagen,
animation: 'caminar',
animations: animaciones,
frameRate: 7,
index: 0
});
};
this.imagen.src = 'img/character.png';
};
Persona.prototype ={
constructor: Persona,
getSprite: function(){
return this.sprite;
}
};
How to fix my trouble?
Thanks.
I think you are calling the method before "img/character.png" is loaded.
You have to define a callback function and launch it when the images are loaded.
The following example does nothing, but it doesn't give an error. Check the onLoad function we pass to Persona.
var Escena = function()
{
this.stage = new Kinetic.Stage({
container: 'simulacion',
width: 578,
height: 200
});
this.layer = new Kinetic.Layer();
var that = this;
this.persona = new Persona(
function(){
that.layer.add( that.persona.getSprite() ); //IT was HERE THE MISTAKE
that.stage.add( that.layer );
console.log(that.persona.getSprite() ) //the sprite has been created
}
);
/*
I moved this to the callback anonymous function
this.persona.getSprite();
this.layer.add( this.persona.getSprite() ); //IT'S HERE THE MISTAKE
this.stage.add( this.layer );
*/
};
var Persona = function(onLoad)
{
this.ancho= 26;
this.alto = 70;
this.sprite ;
var animaciones = {
caminar:
[ { x: 7, y: 38, width: this.ancho, height: this.alto },
{ x: 37, y: 38, width: this.ancho, height: this.alto },
{ x: 68, y: 38, width: this.ancho, height: this.alto },
{ x: 95, y: 38, width: this.ancho, height: this.alto },
{ x: 127, y: 38, width: this.ancho, height: this.alto },
{ x: 157, y: 38, width: this.ancho, height: this.alto },
{ x: 186, y: 38, width: this.ancho, height: this.alto }
]
};
this.imagen = new Image();
var that = this;
this.imagen.onload = function(){
var sprite = new Kinetic.Sprite({
x:250,
y:250,
image: this.imagen,
animation: 'caminar',
animations: animaciones,
frameRate: 7,
index: 0
});
that.sprite = sprite;
onLoad();
};
this.imagen.src = 'img/character.png';
};
Persona.prototype ={
//constructor: Persona,
getSprite: function(){
return this.sprite;
}
};
var escena1 = new Escena();
By the way, spanish is my mother language :-)
Related
I want to draw a polyline:
With arrows
With endpoint control, you can drag and drop, and you will be able to connect with other objects in the future (I don’t know whether to use fabric.js Control or draw a circle by myself)
My current implementation is like this: The line consists of 5 parts:
Two endpoints + two arrows + line body.
When dragging the end point, the arrow position, the arrow angle, and the coordinates of the line body are synchronously modified.
this is demo fabric.PolygonalLine is the key to the problem
var canvas = new fabric.Canvas(document.getElementById("c"))
fabric.PolygonalLine = fabric.util.createClass(fabric.Object, {
// hasControls: false,
// hasBorders: false,
// selectable: true,
points: [],
sControl: null,
eControl: null,
centerControl: null,
// 垂直方向或者水平方向
controlMoveDirection: "",
sArrow: null,
eArrow: null,
initialize: function (points, options = {}) {
this.points = points || []
this.callSuper("initialize", options)
let len = this.points.length
let defaultOpt = {
hasBorders: false,
hasControls: false,
originX: "center",
originY: "center",
}
this.sControl = new fabric.Circle({
fill: null,
radius: 4,
strokeWidth: 1,
stroke: "rgba(57, 87, 239, 0.8)",
left: this.points[0].x,
top: this.points[0].y - 8,
...defaultOpt,
})
this.eControl = new fabric.Circle({
fill: null,
radius: 4,
strokeWidth: 1,
stroke: "rgba(57, 87, 239, 0.8)",
left: this.points[len - 1].x,
top: this.points[len - 1].y + 8,
...defaultOpt,
})
this.sArrow = new fabric.Triangle({
left: this.points[0].x,
top: this.points[0].y,
width: 10,
height: 10,
// selectable: false,
lockMovementX: true,
lockMovementY: true,
...defaultOpt,
})
this.eArrow = new fabric.Triangle({
left: this.points[len - 1].x,
top: this.points[len - 1].y,
width: 10,
height: 10,
angle: 180,
// selectable: false,
lockMovementX: true,
lockMovementY: true,
...defaultOpt,
})
canvas.add(this.sArrow, this.eArrow, this.sControl, this.eControl)
this._bindEvents()
},
_bindEvents() {
this.sControl.on("moving", (e) => {
this.sArrow.set({
left: e.pointer.x,
top: e.pointer.y + 8,
})
this.points[0].x = e.pointer.x
this.points[0].y = e.pointer.y
this.points[1].x = e.pointer.x
canvas.requestRenderAll()
})
this.eControl.on("moving", (e) => {
this.eArrow.set({
left: e.pointer.x,
top: e.pointer.y - 8,
})
this.points[3].x = e.pointer.x
this.points[3].y = e.pointer.y
this.points[2].x = e.pointer.x
canvas.requestRenderAll()
})
this.sControl.on("selected", (e) => {
console.log("---106-11-selected---", this)
})
this.on("selected", (e) => {
console.log("---106---selected--", e)
})
this.on("moving", (e) => {
console.log("---110--moving---", e)
})
},
render(ctx) {
ctx.save()
ctx.beginPath()
ctx.moveTo(this.points[0].x, this.points[0].y)
for (let i = 0; i < this.points.length - 1; i++) {
let point = this.points[i]
ctx.lineTo(point.x, point.y)
}
ctx.lineTo(this.points[3].x, this.points[3].y)
ctx.stroke()
ctx.restore()
},
})
// ---------------------------content---------------------
let rect = new fabric.Rect({
width: 50,
height: 50,
left: 10,
top: 5,
fill: "rgba(255,0,0,0.5)",
})
let circle = new fabric.Circle({
width: 50,
height: 50,
radius: 25,
left: 200,
top: 205,
fill: "#aac",
})
let line = new fabric.PolygonalLine(
[
{
x: 35,
y: 70,
},
{
x: 35,
y: 120,
},
{
x: 225,
y: 120,
},
{
x: 225,
y: 190,
},
],
{
text: "",
stroke: "red",
strokeWidth: 2,
strokeLineCap: "butt",
strokeDashArray: [10, 5],
}
)
canvas.add(rect, circle, line)
canvas.zoomToPoint({ x: 0, y: 0 }, 2)
canvas.renderAll()
canvas {
border: 1px solid #999;
display:inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/460/fabric.js"></script>
<canvas id="c" width="600" height="600"></canvas>
But it feels awkward to do so. Does any friend have a better way? Or provide an idea. Grateful.
I have been trying a few LoDash functions all day but couldn't get the right way to do this. Assigning a key fill to the parent array and also prepend with key levelno and each value of another array below [1,2,3,4,5]
[ [ { rect: 'R202',
x: 163,
y: 1393,
width: 38,
height: 17.5,
grade: 'hf',
accessible: false },
{ rect: 'R214',
x: 163,
y: 1445.5,
width: 38,
height: 17.5,
grade: 'hf',
accessible: false } ],
[ { rect: 'R202',
x: 163,
y: 1393,
width: 38,
height: 17.5,
grade: 'hf',
accessible: false },
{ rect: 'R214',
x: 163,
y: 1445.5,
width: 38,
height: 17.5,
grade: 'hf',
accessible: false } ] ]
with [1,2,3,4,5] into this
{ 'level: [{
"levelno": 1,
"fill": [
{
rect: "R202",
x: 163,
y: 1393,
width: 38,
height: 17.5,
grade: "hf",
accessible: false
}, {
rect: "R214",
x: 163,
y: 1445.5,
width: 38,
height: 17.5,
grade: "hf",
accessible: false
}
]
}, {
"levelno": 2,
"fill": [
{
rect: "R202",
x: 163,
y: 1393,
width: 38,
height: 17.5,
grade: "hf",
accessible: false
}, {
rect: "R214",
x: 163,
y: 1445.5,
width: 38,
height: 17.5,
grade: "hf",
accessible: false
}
]
}]
}
Use vanilla js Array#map or lodash's _.map() to map each sub array to an object in the desired format:
function level(data, levels) {
return {
level: data.map(function(fill, index) {
return {
levelno: levels[index],
fill: fill
};
})
};
}
function level(data, levels) {
return {
level: data.map(function(fill, index) {
return {
levelno: levels[index],
fill: fill
};
})
};
}
var data = [
[{
rect: 'R202',
x: 163,
y: 1393,
width: 38,
height: 17.5,
grade: 'hf',
accessible: false
}, {
rect: 'R214',
x: 163,
y: 1445.5,
width: 38,
height: 17.5,
grade: 'hf',
accessible: false
}],
[{
rect: 'R202',
x: 163,
y: 1393,
width: 38,
height: 17.5,
grade: 'hf',
accessible: false
}, {
rect: 'R214',
x: 163,
y: 1445.5,
width: 38,
height: 17.5,
grade: 'hf',
accessible: false
}]
];
var levels = [1, 2, 3, 4, 5];
var result = level(data, levels);
console.log(result);
The shorter ES6 version:
const level = (data, levels) => ({
level: data.map((fill, index) => ({
levelno: levels[index],
fill
}))
});
I'm trying to create a block game where you select shapes from a menu and place them on the canvas. There is a shape menu where you can drag the shapes onto the canvas. I would like for it to leave the main shape in the menu as it drags a clone onto the canvas. Is this possible? I have created a jsfiddle to help.
JSFIDDLE
window.canvas = new fabric.Canvas('fabriccanvas');
var edgedetection = 10; //pixels to snap
canvas.selection = false;
window.addEventListener('resize', resizeCanvas, false);
function resizeCanvas() {
canvas.setHeight(window.innerHeight);
canvas.setWidth(window.innerWidth);
canvas.renderAll();
}
// resize on init
resizeCanvas();
//Initialize Everything
init();
function init(top, left, width, height, fill) {
var bg = new fabric.Rect({
left: 0,
top: 0,
fill: "#eee",
width: window.innerWidth,
height: 75,
lockRotation: true,
maxHeight: document.getElementById("fabriccanvas").height,
maxWidth: document.getElementById("fabriccanvas").width,
selectable: false,
});
var squareBtn = new fabric.Rect({
top: 6,
left: 18,
width: 40,
height: 40,
fill: '#af3',
lockRotation: true,
originX: 'left',
originY: 'top',
cornerSize: 15,
hasRotatingPoint: false,
perPixelTargetFind: true,
});
var circleBtn = new fabric.Circle({
radius: 20,
fill: '#f55',
top: 6,
left: 105,
});
var triangleBtn = new fabric.Triangle({
width: 40,
height: 35,
fill: 'blue',
top: 10,
left: 190,
});
var sqrText = new fabric.IText("Add Square", {
fontFamily: 'Indie Flower',
fontSize: 14,
fontWeight: 'bold',
left: 6,
top: 50,
selectable: false,
});
var cirText = new fabric.IText("Add Circle", {
fontFamily: 'Indie Flower',
fontSize: 14,
fontWeight: 'bold',
left: 95,
top: 50,
selectable: false,
});
var triText = new fabric.IText("Add Triangle", {
fontFamily: 'Indie Flower',
fontSize: 14,
fontWeight: 'bold',
left: 175,
top: 50,
selectable: false,
});
var shadow = {
color: 'rgba(0,0,0,0.6)',
blur: 3,
offsetX: 0,
offsetY: 2,
opacity: 0.6,
fillShadow: true,
strokeShadow: true
}
window.canvas.add(bg);
bg.setShadow(shadow);
window.canvas.add(sqrText);
window.canvas.add(cirText);
window.canvas.add(triText);
window.canvas.add(squareBtn);
window.canvas.add(circleBtn);
window.canvas.add(triangleBtn);
canvas.forEachObject(function (e) {
e.hasControls = e.hasBorders = false; //remove borders/controls
});
this.canvas.on('object:moving', function (e) {
var obj = e.target;
obj.setCoords(); //Sets corner position coordinates based on current angle, width and height
canvas.forEachObject(function (targ) {
activeObject = canvas.getActiveObject();
if (targ === activeObject) return;
if (Math.abs(activeObject.oCoords.tr.x - targ.oCoords.tl.x) < edgedetection) {
activeObject.left = targ.left - activeObject.currentWidth;
}
if (Math.abs(activeObject.oCoords.tl.x - targ.oCoords.tr.x) < edgedetection) {
activeObject.left = targ.left + targ.currentWidth;
}
if (Math.abs(activeObject.oCoords.br.y - targ.oCoords.tr.y) < edgedetection) {
activeObject.top = targ.top - activeObject.currentHeight;
}
if (Math.abs(targ.oCoords.br.y - activeObject.oCoords.tr.y) < edgedetection) {
activeObject.top = targ.top + targ.currentHeight;
}
if (activeObject.intersectsWithObject(targ) && targ.intersectsWithObject(activeObject)) {
} else {
targ.strokeWidth = 0;
targ.stroke = false;
}
if (!activeObject.intersectsWithObject(targ)) {
activeObject.strokeWidth = 0;
activeObject.stroke = false;
activeObject === targ
}
});
});
}
See Jsfiddle here, I add the function to do this.
function draggable(object) {
object.on('mousedown', function() {
var temp = this.clone();
temp.set({
hasControls: false,
hasBorders: false,
});
canvas.add(temp);
draggable(temp);
});
object.on('mouseup', function() {
// Remove an event handler
this.off('mousedown');
// Comment this will let the clone object able to be removed by drag it to menu bar
// this.off('mouseup');
// Remove the object if its position is in menu bar
if(this.top<=75) {
canvas.remove(this);
}
});
}
draggable(squareBtn);
draggable(circleBtn);
draggable(triangleBtn);
This is my code :
var playerTimerGroup = new Kinetic.Group({
x:420,
y:350
});
var animations = {
idle: [{
x: 408,
y: 1420,
width: 55,
height: 55
}, {
x: 463,
y: 1420,
width: 55,
height: 55
}, {
x: 518,
y: 1420,
width: 55,
height: 55
}, {
x: 573,
y: 1420,
width: 55,
height: 55
}, {
x: 628,
y: 1420,
width: 55,
height: 55
}]
};
var timer = new Kinetic.Sprite({
x: 0,
y: 0,
image: SpriteImaage,
animation: 'idle',
animations: animations,
frameRate: 7,
index: 0
});
playerTimerGroup.add(timer);
layer.add(playerTimerGroup);
stage.add(self.layer);
timer.start();
Here i want to show a text (seconds) which keep changing for every frame.
How to achieve this ?
(I mean when first animation start I want to show 1, on second I want to show 2 and so on... till last animation.)
I tried this :
_.each(animations.idle, function(value, index){
timer.afterFrame(index, function(){
console.log(index);
});
});
but this runs only for last index..
any way to bind timer.afterFrame for every index or call timer.afterFrame for every index ?
var playerTimerGroup = new Kinetic.Group({
x: 420,
y: 350
});
var animations = {
idle: [{
x: 408,
y: 1420,
width: 55,
height: 55
}, {
x: 463,
y: 1420,
width: 55,
height: 55
}, {
x: 518,
y: 1420,
width: 55,
height: 55
}, {
x: 573,
y: 1420,
width: 55,
height: 55
}, {
x: 628,
y: 1420,
width: 55,
height: 55
}]
};
var timer = new Kinetic.Sprite({
x: 0,
y: 0,
image: SpriteImaage,
animation: 'idle',
animations: animations,
frameRate: 1,
index: 0
});
creating a text object here, which will change on every second:
var timerText = new Kinetic.Text({
x: 15,
y: 15,
text: '20',
fontSize: 25,
fontFamily: 'Calibri',
fill: 'white'
});
i = 1;
time = 0;
creating an animation object here which will change the text on every second:
var timerTextAnimation = new Kinetic.Animation(function(frame) {
timerText.setText(i);
if (frame.time - time >= 1000) {
i++;
time = frame.time;
}
}, layer);
timerTextAnimation.start();
playerTimerGroup.add(timer);
playerTimerGroup.add(timerText);
layer.add(playerTimerGroup);
stage.add(self.layer);
timer.start();
stopping all the animations here:
timer.afterFrame(animations.idle.length - 1, function() {
timer.stop();
timerTextAnimation.stop();
});
This works for me.
hope it also helps others.
I want to use Angularjs with Kineticjs. The problem is that, it seems angular is overriding mouse events on my stage object.
The following html works fine as stand alone file, you can test it. But if you put this as an Angularjs template, the mouse events stop working.
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
#container {
background-color: black;
background-image: url("http://www.html5canvastutorials.com/demos/assets/line-building.png");
background-position: 1px 0px;
background-repeat: no-repeat;
width: 580px;
height: 327px;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.5.1.min.js"></script>
<script defer="defer">
function getData() {
return {
'1st Floor': {
color: 'blue',
points: [366, 298, 500, 284, 499, 204, 352, 183, 72, 228, 74, 274]
},
'2nd Floor': {
color: 'red',
points: [72, 228, 73, 193, 340, 96, 498, 154, 498, 191, 341, 171]
},
'3rd Floor': {
color: 'yellow',
points: [73, 192, 73, 160, 340, 23, 500, 109, 499, 139, 342, 93]
},
'Gym': {
color: 'green',
points: [498, 283, 503, 146, 560, 136, 576, 144, 576, 278, 500, 283]
}
}
}
function updateTooltip(tooltip, x, y, text) {
tooltip.getText().setText(text);
tooltip.setPosition(x, y);
tooltip.show();
}
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 325
});
var shapesLayer = new Kinetic.Layer();
var tooltipLayer = new Kinetic.Layer();
var tooltip = new Kinetic.Label({
opacity: 0.75,
visible: false,
listening: false,
text: {
text: '',
fontFamily: 'Calibri',
fontSize: 18,
padding: 5,
fill: 'white'
},
rect: {
fill: 'black',
pointerDirection: 'down',
pointerWidth: 10,
pointerHeight: 10,
lineJoin: 'round',
shadowColor: 'black',
shadowBlur: 10,
shadowOffset: 10,
shadowOpacity: 0.5
}
});
tooltipLayer.add(tooltip);
// get areas data
var areas = getData();
// draw areas
for(var key in areas) {
var area = areas[key];
var points = area.points;
var shape = new Kinetic.Polygon({
points: points,
fill: area.color,
opacity: 0,
// custom attr
key: key
});
shapesLayer.add(shape);
}
stage.add(shapesLayer);
stage.add(tooltipLayer);
stage.on('mouseover', function(evt) {
var shape = evt.targetNode;
shape.setOpacity(0.5);
shapesLayer.draw();
});
stage.on('mouseout', function(evt) {
var shape = evt.targetNode;
shape.setOpacity(0);
shapesLayer.draw();
tooltip.hide();
tooltipLayer.draw();
});
stage.on('mousemove', function(evt) {
var shape = evt.targetNode;
var mousePos = stage.getMousePosition();
var x = mousePos.x;
var y = mousePos.y - 5;
updateTooltip(tooltip, x, y, shape.attrs.key);
tooltipLayer.batchDraw();
});
</script>
How does event handling work on angular? And how could I prevent from overriding events like:
stage.on('mouseover', function(evt) {
var shape = evt.targetNode;
shape.setOpacity(0.5);
shapesLayer.draw();
});
Thanks!
Ok, it seems Angular was capturing all DOM events.
The solution is to put Kineticjs tag before angular.js(and probably before jQuery too), and be sure you don't declare the tag again elsewhere in any view.