KineticJS dump object properties - javascript

Is there any methods in KineticJS or in javascript that will allow you to dump object properties and their values.enter code here

All properties that have been set on a Node are in the yourObject.attrs object.
Heres example code:
<!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-v4.7.2.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 layer = new Kinetic.Layer();
stage.add(layer);
var circle1 = new Kinetic.Circle({
x:100,
y:100,
radius: 30,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
layer.add(circle1);
layer.draw();
enumerateAttributes(circle1);
function enumerateAttributes(node){
var text="";
var attrs=circle1.getAttrs();
for (key in attrs) {
if (attrs.hasOwnProperty(key)) {
text+=(key+"=="+attrs[key]+"\n");
}
}
alert(text);
}
}); // end $(function(){});
</script>
</head>
<body>
<div id="container"></div>
</body>
</html>

Related

fabric js canvas doesn't render images

I put an image and a textbox on a FabricJS canvas, but only the textbox is rendered. I also tried to export the whole canvas as an image, but still, only the textbox showed up. If I print out all objects on canvas in the console, the image instance is there, but why it's not rendered? Here is the JS Fiddle,
Here is the full HTML file
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.3.1/fabric.min.js"></script>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
</head>
<style>
#c {
border: 2px solid black;
}
#render {
border: 2px solid red;
}
</style>
<body>
<h2>This is the test image</h2>
<img id='testImg' src="https://picsum.photos/seed/123/50/50" crossorigin="anonymous">
<h2>Fabric canvas</h2>
<canvas id='c'></canvas>
<h2>Save canvas as an image</h2>
<div id='render'></div>
<script>
var canvas = new fabric.Canvas('c');
canvas.setHeight(200);
canvas.setWidth(200);
//add image
var imgEle = $("#testImg")[0];
var newImg = new fabric.Image(imgEle, {
left: 0,
top:0
});
canvas.add(newImg);
canvas.renderAll();
//add new textbox
var newTextBox = new fabric.Textbox("Hello text box", {
left: 0,
top: 100,
width: 200
});
canvas.add(newTextBox);
canvas.renderAll();
//two objects are stored in canvas
console.log(JSON.stringify(canvas));
//show the canvas image, only one object shows
var canvasUrl = canvas.toDataURL('png');
var canvasImg = $("<img id=canvasImg>");
canvasImg.attr('src', canvasUrl);
canvasImg.appendTo("#render");
</script>
</body>
</html>
In your code you are not waiting for the image to load, so what happens is that the new fabric.Image executes before the image has fully loaded, that is why you only see the text.
We need an onload on the image.
See the code below:
var canvas = new fabric.Canvas('c');
canvas.setHeight(120);
canvas.setWidth(200);
$("#testImg")[0].onload = function () {
var newImg = new fabric.Image(this, { left: 10, top: 10 });
canvas.add(newImg);
canvas.renderAll();
}
var newTextBox = new fabric.Textbox("Hello", { left: 0, top: 70, width: 90 });
canvas.add(newTextBox);
canvas.renderAll();
#c {
border: 2px solid black;
}
#render {
border: 2px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js"></script>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
This is the test image
<img id='testImg' src="https://picsum.photos/seed/123/50/50" crossorigin="anonymous">
<canvas id='c'></canvas>

fabric js multi-select moving does not fire single object's moving event

if I have multiple pairs of objects, say, (A1, A2), (B1, B2), (C1, C2), what I want is that when object A1/B1/C1 is moving, object A2/B2/C2 is moving as well. And when I multi-select objects, say, A1 and B1, and move them around, A2 and B2 don't move at all because the single object event doesn't fire. How can I fix it?
Thanks!
Here's a pen to demonstrate: https://codepen.io/taineleau/pen/PdxRKX
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Fabric JS playground</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.0.0-rc.3/fabric.js'></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
<style>
canvas {
position:relative;
display:inline-block;
width:100%;
}
</style>
</head>
<body>
<canvas id="fabriccanvas" width="1000px" height="1000px"></canvas>
<script>
function bind(obj1, obj2) {
obj1.on("moving", function () {
obj2.left = this.left + 50;
obj2.top = this.top;
});
}
var canvas = new fabric.Canvas("fabriccanvas");
var A1 = new fabric.Rect({
width: 200, height: 100, left: 0, top: 50, angle: 30,
fill: 'rgb(255,0,0)'
});
var A2 = new fabric.Rect({
width: 200, height: 100, left: 50, top: 50, angle: 30,
fill: 'rgba(255,0,0,0.5)',
selectable: false,
});
var B1 = new fabric.Rect({
width: 100, height: 100, left: 35, top: 25, angle: -10,
fill: 'rgb(0,200,0)'
});
var B2 = new fabric.Rect({
width: 100, height: 100, left: 85, top: 25, angle: -10,
fill: 'rgba(0,200,0,0.5)',
selectable: false,
});
bind(A1, A2);
bind(B1, B2);
canvas.add(A1, A2, B1, B2);
</script>
</body>
</html>
if I multi-select A1 and B1 and moving them, A2 and B2 don't move at all
You can use on.("object:moving"), check if it matches an object you want to bind to, or if it contains one, and then update appropriately. If it contains an object you want to bind to, you have to offset it by the position and size of the container itself.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Fabric JS playground</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.0.0-rc.3/fabric.js'></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
<style>
canvas {
position: relative;
display: inline-block;
width: 100%;
}
</style>
</head>
<body>
<canvas id="fabriccanvas" width="1000px" height="1000px"></canvas>
<script>
function bind(obj1, obj2) {
canvas.on('object:moving', function(event) {
if (obj1 == event.target) {
obj2.left = obj1.left + 50;
obj2.top = obj1.top;
} else if ("_objects" in event.target) {
if (event.target["_objects"].includes(obj1)) {
obj2.left = obj1.left + event.target.left + event.target.width / 2 + 50;
obj2.top = obj1.top + event.target.top + event.target.height / 2;
}
}
});
}
var canvas = new fabric.Canvas("fabriccanvas");
var A1 = new fabric.Rect({
width: 200,
height: 100,
left: 0,
top: 50,
angle: 30,
fill: 'rgb(255,0,0)'
});
var A2 = new fabric.Rect({
width: 200,
height: 100,
left: 50,
top: 50,
angle: 30,
fill: 'rgba(255,0,0,0.5)',
selectable: false,
});
var B1 = new fabric.Rect({
width: 100,
height: 100,
left: 35,
top: 25,
angle: -10,
fill: 'rgb(0,200,0)'
});
var B2 = new fabric.Rect({
width: 100,
height: 100,
left: 85,
top: 25,
angle: -10,
fill: 'rgba(0,200,0,0.5)',
selectable: false,
});
bind(A1, A2);
bind(B1, B2);
canvas.add(A1, A2, B1, B2);
</script>
</body>
</html>

rect.set the hover area is not updated

How can I use the object.set({}) and then have hover area update.
For example when I move a rect to the left, visually the rect has moved but the hover area is still based on the previous left position.
Do I need to do an additional render?
I figured canvas.renderAll() would be enough
Here is a demo
const canvas = new fabric.Canvas(document.getElementById("myCanvas"));
var rect = new fabric.Rect({
width: 100,
height: 100,
fill: "red",
left: 25,
top: 25
});
canvas.add(rect);
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
rect.set({ left: 125 });
canvas.renderAll();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.6/fabric.min.js"></script>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<div id="app"></div>
<button id="btn">move left</button>
<canvas id="myCanvas" width="300" height="200" ></canvas>
</body>
</html>
https://codesandbox.io/s/rlo3ojwvp4
You need to set coordinates after move,check When-to-call-setCoords
DEMO
const canvas = new fabric.Canvas(document.getElementById("myCanvas"));
var rect = new fabric.Rect({
width: 100,
height: 100,
fill: "red",
left: 25,
top: 25
});
canvas.add(rect);
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
rect.set({ left: 125 });
rect.setCoords();
canvas.renderAll();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.6/fabric.min.js"></script>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
<style>
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<div id="app"></div>
<button id="btn">move left</button>
<canvas id="myCanvas" width="300" height="200" ></canvas>
</body>
</html>

How to set div back after being pushed by Fabric.js canvas.setHeigth or setWidth?

I'm going to make a twibbon-like website using Fabric.js for my school so students can post a picture and download it with my school campaign image on it and share it to Instagram.
to download the image in high resolution and maintain the canvas to be small enough to fit the screen, I'm using this solution I found from another stackoverflow question here
the problem is the canvas is still small but it push my div or buttons
away by the value of canvas.setWidth or setHeigth.
here's my page looks like.
how can i fix this and bring back my buttons?
here's my code :
.canvas-container canvas {
width: 400px !important;
height: 500px !important;
border: 1px solid blue;
}
body{
font-family: 'Helvetica', Tahoma, Geneva, Verdana, sans-serif;
}
h1{
color: rgb(119, 214, 124);
}
.site{
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-gap: 1em;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="cssTest.css">
<title>Twibbon</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.3/fabric.min.js"></script>
<script src="FileSaver.js"></script>
<script src="canvas-toBlob.js"></script>
</head>
<body>
<div class="site">
<h1 class="title">Fortafaste Campaign</h1>
<canvas id="c"></canvas>
<input type="file" id="d" class="button">
<input id="download" type="button" value="download" />
<h2 class="contact">instagram</h2>
</div>
<script>
var canvas = new fabric.Canvas('c');
canvas.setWidth(1080);
canvas.setHeight(1350);
var canvasWrapper = document.getElementById('c');
var canvasWrapperWidth = canvasWrapper.clientWidth;
$(function () {
$(":file").change(function () {
if (this.files && this.files[0]) {
var reader = new FileReader();
reader.onload = imageIsLoaded;
reader.readAsDataURL(this.files[0]);
}
});
});
fabric.Image.fromURL('twibbon2 fortafaste.png',function(img){
img.scaleToWidth(canvas.getWidth());
canvas.setOverlayImage(img, canvas.renderAll.bind(canvas));
});
function imageIsLoaded(e) {
fabric.Image.fromURL(e.target.result,function(img){
var aspectRatio = 1350/1080;
var factor = 1080 / img.width;
img.set({
scaleX: factor,
scaleY: factor
});
canvas.add(img);
canvas.sendToBack(img);
});
};
$("#download").click(function(){
$("#c").get(0).toBlob(function(blob){
saveAs(blob, "myIMG.png");
});
});
</script>
</body>
</html>
I recommend you to simplify your design...
Keep all the information and action buttons on top and the canvas below, here is an example:
.canvas-container canvas {
width: 400px !important;
height: 500px !important;
border: 1px solid blue;
}
body{
font-family: 'Helvetica', Tahoma, Geneva, Verdana, sans-serif;
}
h1{
color: rgb(119, 214, 124);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="cssTest.css">
<title>Twibbon</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.3/fabric.min.js"></script>
<script src="FileSaver.js"></script>
<script src="canvas-toBlob.js"></script>
</head>
<body>
<div class="site">
<div>
<h1 class="title">Fortafaste Campaign</h1>
<input type="file" id="d" class="button">
<input id="download" type="button" value="download" />
<h2 class="contact">instagram</h2>
</div>
<canvas id="c"></canvas>
</div>
<script>
var canvas = new fabric.Canvas('c');
canvas.setWidth(1080);
canvas.setHeight(1350);
var canvasWrapper = document.getElementById('c');
var canvasWrapperWidth = canvasWrapper.clientWidth;
$(function () {
$(":file").change(function () {
if (this.files && this.files[0]) {
var reader = new FileReader();
reader.onload = imageIsLoaded;
reader.readAsDataURL(this.files[0]);
}
});
});
fabric.Image.fromURL('twibbon2 fortafaste.png',function(img){
img.scaleToWidth(canvas.getWidth());
canvas.setOverlayImage(img, canvas.renderAll.bind(canvas));
});
function imageIsLoaded(e) {
fabric.Image.fromURL(e.target.result,function(img){
var aspectRatio = 1350/1080;
var factor = 1080 / img.width;
img.set({
scaleX: factor,
scaleY: factor
});
canvas.add(img);
canvas.sendToBack(img);
});
};
$("#download").click(function(){
$("#c").get(0).toBlob(function(blob){
saveAs(blob, "myIMG.png");
});
});
</script>
</body>
</html>

Uncaught TypeError: Cannot call method 'add' of undefined (Kinetic js)

I am trying to make a game where multiple circle objects are made by use of kineticjs.
Here's the html:
<!DOCTYPE html>
<html>
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="../css/style.css"/>
</head>
<body>
<!--<canvas id="canvasRegn" width="600" height="450" style="margin:100px;"></canvas>-->
<div id="container" style="width: auto; height: auto; background:#000; margin:auto; float:left;"></div>
<script src="../js/jquery.min.js"></script>
<script src="../js/kinetic-v5.0.0.min.js"></script>
<script type="text/javascript" src="../js/rain2.js"></script>
</body>
</html>
My logic in js is->
I push "enemy" (circular kineticjs objects) into the array "enemyArmada" which is done in the function "createEnemy()".
The the "draw" function is called every 35ms using a setInterval,where the required position and speed are altered to create the moving effect.
Here's the js code:
var x = 0;
var y = 0;
var noOfEnemies = 2;
var enemyArmada = new Array();
createEnemy();
var stage = new Kinetic.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
listening: true
});
var layer = new Kinetic.Layer({
listening: true
});
function createEnemy() {
for (var i = 0; i < noOfEnemies; i++) {
var enemy = new Kinetic.Circle({
x: Math.random() * 1200,
y: Math.random() * 5,
radius: 6,
fill: 'red',
stroke: 'black',
speed: 3 + Math.random() * 5
});
enemyArmada.push(enemy);
}
// setInterval(draw, 35);
draw();
}
function draw() {
for (var i = 0; i < noOfEnemies; i++)
{
console.log(enemyArmada[i].getPosition().x);
enemyArmada[i].setPosition({x:enemyArmada[i].getPosition().x + 10, y:enemyArmada[i].getPosition().y + 10});
console.log(enemyArmada[i].getPosition().x + " " + enemyArmada[i].getPosition().y + "" + enemyArmada[i].speed);
}
layer.add(enemyArmada);
layer.draw();
stage.add(layer);
}
Here's the css:
html {
color:#000;
background:#222222;
}
a {
cursor:pointer;
list-style: none;
}
html, body {
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
overflow: hidden;
}
html, body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td {
margin:0;
padding:0;
}
#container {
background:#000;
margin:auto;
cursor:none;
float:left;
width:auto;
height: auto;
}
My ultimate aim is to create enemies like in this game:
http://www.sinuousgame.com/
Here's the fiddle:
http://jsfiddle.net/R3p5s/3/
You are calling the createEnemy before the layer object is created, so the variable layer value is undefined when createEnemy is called..
var stage = new Kinetic.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
listening: true
});
var layer = new Kinetic.Layer({
listening: true
});
//need to call it after the layer is created
createEnemy();
Demo: Fiddle
You also may want to read about hoisting

Categories

Resources