Kinetic.js – creating a grid - javascript

I'm new to Kintetic.js and I'm trying to do a grid. The width is 800px and the height is 400px. And I want squares (20x20) to cover that area. Every square has a 1px border. So something like this:
var box = new Kinetic.Rect({
width: 20,
height: 20,
fill: 'transparent',
stroke: 'rgba(0, 0, 0, 0.02)'
});
And to fill the canvas, I have a crappy for-loop like this:
for (var i = 0; i <= this.field.getWidth(); i = i + 20) {
for (var i2 = 0; i2 <= this.field.getHeight(); i2 = i2 + 20) {
var cbox = box.clone({x: i, y: i2});
this.grid.add(cbox);
}
}
this.grid is a Kinetic.Layer. The first problem with this code is that it is very slow and I get like a 500ms delay before the grid shows up. But the worst thing is that if I put an mouseover and mouseout event on the cbox to change the fill color, that renders really really slow. This is how I do it:
cbox.on('mouseover', function () {
this.setFill('black');
self.grid.draw();
});
cbox.on('mouseout', function () {
this.setFill('transparent');
self.grid.draw();
});
So my question is how can I improve the code and performance of this?

How about to make grid with lines and use one rect for cursor highlighting?
Here i wrote the example for you:
http://jsfiddle.net/e_aksenov/R72Xu/30/
var CELL_SIZE = 35,
w = 4,
h = 5,
W = w * CELL_SIZE,
H = h * CELL_SIZE;
var make_grid = function(layer) {
var back = new Kinetic.Rect({
x: 0,
y: 0,
width: W,
height: H,
fill: "yellow"
});
layer.add(back);
for (i = 0; i < w + 1; i++) {
var I = i * CELL_SIZE;
var l = new Kinetic.Line({
stroke: "black",
points: [I, 0, I, H]
});
layer.add(l);
}
for (j = 0; j < h + 1; j++) {
var J = j * CELL_SIZE;
var l2 = new Kinetic.Line({
stroke: "black",
points: [0, J, W, J]
});
layer.add(l2);
}
return back; //to attach mouseover listener
};
var cursor_bind = function(layer, grid_rect, rect) {
grid_rect.on('mouseover', function(e) {
var rx = Math.floor(e.x / CELL_SIZE);
var ry = Math.floor(e.y / CELL_SIZE);
rect.setPosition(rx * CELL_SIZE, ry * CELL_SIZE);
layer.draw();
});
};
var stage = new Kinetic.Stage({
container: "kinetic",
width: 800,
height: 600,
draggable: true
});
var layer = new Kinetic.Layer();
var rect = new Kinetic.Rect({
x: 0,
y: 0,
width: CELL_SIZE,
height: CELL_SIZE,
fill: "#00D2FF",
stroke: "black",
strokeWidth: 4
});
var gr = make_grid(layer);
cursor_bind(layer, gr, rect);
// add the shape to the layer
layer.add(rect);
// add the layer to the stage
stage.add(layer);​

Related

Converting 2D Image into 3D Using three.js and Other Tools [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I have an image of a data visualization that I want to make into 3D. I was wondering how I would be able to convert this image into 3D using three.js and other possible tools. I have no idea what to do.
I heard that there is something called WebGL Javascript API so maybe I can use that but I don't know how to begin and whether or not it can be used in this situation. I also read somewhere that you cannot make a 3D image from a 2D one without some information about the 3rd dimension. This missing information can come from a second photo, an AI software, or a 3D digital model.
I also read that I can make a 3D model of the image in Blender and then import it into three.js.
Does anyone know how I can do this?
I want to try to do the coding in JS fiddle if it's possible unless other software/tools are needed outside of JS fiddle. I see that you can import the three.js library there.
The current visualization I have made is
My code for this visualization if you need it is:
$(function() {
var dataEx = [
['1 Visit', 352000],
['2 Visits', 88000],
['3+ Visits', 42000]
],
len = dataEx.length,
sum = 0,
minHeight = 0.05,
data = [];
//specify your percent of prior visit value manually here:
var perc = [100, 25, 48];
for (var i = 0; i < len; i++) {
sum += dataEx[i][1];
}
for (var i = 0; i < len; i++) {
var t = dataEx[i],
r = t[1] / sum;
data[i] = {
name: t[0],
y: (r > minHeight ? t[1] : sum * minHeight),
percent: perc[i], // <----- this here is manual input
//percent: Math.round(r * 100), <--- this here is mathematical
label: t[1]
}
}
console.log(dataEx, data)
$('#container').highcharts({
chart: {
type: 'funnel',
marginRight: 100,
events: {
load: function() {
var chart = this;
Highcharts.each(chart.series[0].data, function(p, i) {
var bBox = p.dataLabel.getBBox()
p.dataLabel.attr({
x: (chart.plotWidth - chart.plotLeft) / 2,
'text-anchor': 'middle',
y: p.labelPos.y - (bBox.height / 2)
})
})
},
redraw: function() {
var chart = this;
Highcharts.each(chart.series[0].data, function(p, i) {
p.dataLabel.attr({
x: (chart.plotWidth - chart.plotLeft) / 2,
'text-anchor': 'middle',
y: p.labelPos.y - (bBox.height / 2)
})
})
}
},
},
//Manually changing the default colors of each category of series
colors: ['#FF5733', '#FFA533', '#1FC009'],
title: {
text: 'New Guest Return Funnel',
x: -45
},
credits: {
enabled: false
},
tooltip: {
//enabled: false
formatter: function() {
return '<b>' + this.key +
'</b><br/>Percent of Prior Visit: '+ this.point.percent + '%<br/>Guests: ' + Highcharts.numberFormat(this.point.label, 0);
}
},
plotOptions: {
series: {
allowPointSelect: true,
borderWidth: 12,
animation: {
duration: 400
},
dataLabels: {
enabled: true,
connectorWidth: 0,
distance: 0,
formatter: function() {
var point = this.point;
console.log(point);
return '<b>' + point.name + '</b> (' + Highcharts.numberFormat(point.label, 0) + ')<br/>' + point.percent + '%';
},
minSize: '10%',
color: 'black',
softConnector: true
},
neckWidth: '30%',
neckHeight: '0%',
width: '50%',
height: '110%'
//old options are as follows:
//neckWidth: '50%',
//neckHeight: '50%',
//-- Other available options
//height: '200'
// width: pixels or percent
}
},
legend: {
enabled: false
},
series: [{
name: 'Unique users',
data: data
}]
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/modules/funnel.js"></script>
<script src="http://code.highcharts.com/modules/exporting.js"></script>
<div id="container" style="width: 500px; height: 400px; margin: 0 auto"></div>
I made you a quick and dirty example. Hopefully it gives you some ideas.
var dataEx = [
['1 Visit', 352000],
['2 Visits', 88000],
['3+ Visits', 42000]
],
len = dataEx.length,
sum = 0,
minHeight = 0.05,
data = [];
//specify your percent of prior visit value manually here:
var perc = [100, 25, 48];
for (var i = 0; i < len; i++) {
sum += dataEx[i][1];
}
for (var i = 0; i < len; i++) {
var t = dataEx[i],
r = t[1] / sum;
data[i] = {
name: t[0],
y: (r > minHeight ? t[1] : sum * minHeight),
percent: perc[i], // <----- this here is manual input
//percent: Math.round(r * 100), <--- this here is mathematical
label: t[1]
}
}
console.log(dataEx, data)
var renderer = new THREE.WebGLRenderer();
var w = 300;
var h = 200;
renderer.setSize(w, h);
document.body.appendChild(renderer.domElement);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
45, // Field of view
w / h, // Aspect ratio
0.1, // Near
10000 // Far
);
controls = new THREE.OrbitControls(camera, renderer.domElement);
camera.position.set(0, 20, 15);
camera.lookAt(new THREE.Vector3(0, 60, 0));
controls.target.set(0, 10, 0);
var light = new THREE.PointLight(0xFFFFFF);
light.position.set(20, 20, 20);
scene.add(light);
var light1 = new THREE.AmbientLight(0x808080);
light1.position.set(20, 20, 20);
scene.add(light1);
var light2 = new THREE.PointLight(0xFFFFFF);
light2.position.set(-20, 20, -20);
scene.add(light2);
var light3 = new THREE.PointLight(0xFFFFFF);
light3.position.set(-20, -20, -20);
scene.add(light3);
function makeCanvasTexture(color, text) {
var canvas = document.createElement('canvas');
canvas.width = canvas.height = 256;
var ctx = canvas.getContext('2d');
ctx.textAlign = "center";
ctx.fillStyle = color;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'black';
ctx.font = "30px Arial";
ctx.fillText(text, (canvas.width / 2) | 0, (canvas.height / 2) | 0);
var tex = new THREE.Texture(canvas)
tex.minFilter = THREE.LinearMipMapLinearFilter;
tex.magFilter = THREE.LinearFilter;
tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
tex.needsUpdate = true;
return tex;
}
function makePlane(color, text, percent, position) {
var geom = new THREE.PlaneGeometry(1, 1);
var material = new THREE.MeshLambertMaterial({
color: 'white',
side: THREE.DoubleSide,
map: makeCanvasTexture(color, text)
});
var npct = percent / 100;
material.map.repeat.set(1, npct);
material.map.offset.set(0, 0.5 * (1 - npct));
var mesh = new THREE.Mesh(geom, material);
mesh.position.y = position;
mesh.scale.y *= percent / 100;
return mesh;
}
renderer.setClearColor(0xdddddd, 1);
var root = new THREE.Object3D();
scene.add(root);
root.scale.multiplyScalar(10);
var yOffset = 0;
var colors = ['red', 'green', 'yellow']
for (var i = 0; i < data.length; i++) {
yOffset += data[i].percent / 200
var plane = makePlane(colors[i], data[i].name, data[i].percent, yOffset);
root.add(plane);
yOffset += data[i].percent / 200
yOffset += 0.05
}
(function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
var pnow = performance.now()*0.001;
controls.target.y = (Math.sin(pnow)*10)+10
})();
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>

Draggable sprites on a wallpaper

I'm trying to reproduce something like this: http://carbure.co/.
After inspection the website uses matter.js, a physics engine. Below is a (failed) code attempt, and I'm having trouble getting it to work given the terrible docs.
Does anyone have any idea how else I can achieve this?
Many thanks
$(window).load(function() {
var w = $(window).innerWidth();
var h = $(window).innerHeight();
// Matter.js module aliases
var Engine = Matter.Engine;
var World = Matter.World;
var Bodies = Matter.Bodies;
var Body = Matter.Body;
var Constraint = Matter.Constraint;
var Composite = Matter.Composite;
var Composites = Matter.Composites;
var MouseConstraint = Matter.MouseConstraint;
// create a Matter.js engine
var engine = Engine.create({
render: {
element: document.body,
options: {
width: w,
height: h,
wireframes: false,
background: '#fff'
}
}
});
// add a mouse controlled constraint
var mouseConstraint = MouseConstraint.create(engine);
World.add(engine.world, mouseConstraint);
var addToWorld = [];
// create random poly's and a ground
var ranPolygons = Math.random() * 10 + 5 >> 0;
var prevPoly;
for (var i = 0; i < ranPolygons; i++) {
var polyRadius = Math.random() * 40 + 40 >> 0;
var polySides = 1;
var x = Math.random() * (w - polyRadius * 2) + polyRadius >> 0;
var y = Math.random() * (h / 2 - polyRadius * 2) + polyRadius >> 0;
var isStatic = Math.random() * 1 < 0.2;
var poly = Bodies.polygon(x, y, polySides, polyRadius, {
render: {
fillStyle: isStatic ? '#0134CB' : makePattern(),
strokeStyle: isStatic ? 'transparent' : '#0134CB',
lineWidth: Math.random() * 5 + 2 >> 0
},
density: Math.random() * 0.1,
isStatic: isStatic,
restitution: Math.random() * 1
});
addToWorld.push(poly);
// add borders
var border = 5;
var halfBorder = border / 2;
var borders = [
Bodies.rectangle(w / 2, halfBorder, w + border, border, {
isStatic: true,
render: {
fillStyle: 'transparent',
strokeStyle: 'transparent'
}
}),
Bodies.rectangle(w / 2, h - halfBorder, w + border, border, {
isStatic: true,
render: {
fillStyle: 'transparent',
strokeStyle: 'transparent'
}
}),
Bodies.rectangle(halfBorder, h / 2, border, h + border, {
isStatic: true,
render: {
fillStyle: 'transparent',
strokeStyle: 'transparent'
}
}),
Bodies.rectangle(w - halfBorder, h / 2, border, h + border, {
isStatic: true,
render: {
fillStyle: 'transparent',
strokeStyle: 'transparent'
}
}),
];
addToWorld = addToWorld.concat(borders);
// add all of the bodies to the world
World.add(engine.world, addToWorld);
// run the engine
runner = Engine.run(engine)
// setTimeout(ranGrav, 2000);
engine.world.gravity.y = 0;
engine.world.gravity.x = 0;
$(engine.render.canvas).css({
width: '100%',
height: '100vh'
})
});
I got your code running. It had a number of issues. First and foremost, the missing bracket belonged to the loop:
for (var i = 0; i < ranPolygons; i++) {
Besides that I also had to run the renderer:
Render.run(render);
And I got rid of this bit, because it was unnecessary and was throwing a warning:
$(engine.render.canvas).css({
width: '100%',
height: '100vh'
});
https://jsfiddle.net/jx3vn7da/

kineticjs performance lag

I am working on a radial control similar to the HTML5 wheel of fortune example. I've modified the original here with an example of some additional functionality I require: http://jsfiddle.net/fEm9P/ When you click on the inner kinetic wedges they will shrink and expand within the larger wedges. Unfortunately when I rotate the wheel it lags behind the pointer. It's not too bad here but it's really noticeable on a mobile.
I know this is due to the fact that I'm not caching the wheel. When I do cache the wheel (uncomment lines 239-249) the inner wedges no longer respond to mouse/touch but the response on rotation is perfect. I have also tried adding the inner wedges to a separate layer and caching the main wheel only. I then rotate the inner wheel with the outer one. Doing it this way is a little better but still not viable on mobile.
Any suggestions would be greatly appreciated.
Stephen
//constants
var MAX_ANGULAR_VELOCITY = 360 * 5;
var NUM_WEDGES = 25;
var WHEEL_RADIUS = 410;
var ANGULAR_FRICTION = 0.2;
// globals
var angularVelocity = 360;
var lastRotation = 0;
var controlled = false;
var target, activeWedge, stage, layer, wheel,
pointer, pointerTween, startRotation, startX, startY;
var currentVolume, action;
function purifyColor(color) {
var randIndex = Math.round(Math.random() * 3);
color[randIndex] = 0;
return color;
}
function getRandomColor() {
var r = 100 + Math.round(Math.random() * 55);
var g = 100 + Math.round(Math.random() * 55);
var b = 100 + Math.round(Math.random() * 55);
var color = [r, g, b];
color = purifyColor(color);
color = purifyColor(color);
return color;
}
function bind() {
wheel.on('mousedown', function(evt) {
var mousePos = stage.getPointerPosition();
angularVelocity = 0;
controlled = true;
target = evt.targetNode;
startRotation = this.rotation();
startX = mousePos.x;
startY = mousePos.y;
});
// add listeners to container
document.body.addEventListener('mouseup', function() {
controlled = false;
action = null;
if(angularVelocity > MAX_ANGULAR_VELOCITY) {
angularVelocity = MAX_ANGULAR_VELOCITY;
}
else if(angularVelocity < -1 * MAX_ANGULAR_VELOCITY) {
angularVelocity = -1 * MAX_ANGULAR_VELOCITY;
}
angularVelocities = [];
}, false);
document.body.addEventListener('mousemove', function(evt) {
var mousePos = stage.getPointerPosition();
var x1, y1;
if(action == 'increase') {
x1 = (mousePos.x-(stage.getWidth() / 2));
y1 = (mousePos.y-WHEEL_RADIUS+20);
var r = Math.sqrt(x1 * x1 + y1 * y1);
if (r>500){
r=500;
} else if (r<100){
r=100;
};
currentVolume.setRadius(r);
layer.draw();
} else {
if(controlled && mousePos && target) {
x1 = mousePos.x - wheel.x();
y1 = mousePos.y - wheel.y();
var x2 = startX - wheel.x();
var y2 = startY - wheel.y();
var angle1 = Math.atan(y1 / x1) * 180 / Math.PI;
var angle2 = Math.atan(y2 / x2) * 180 / Math.PI;
var angleDiff = angle2 - angle1;
if ((x1 < 0 && x2 >=0) || (x2 < 0 && x1 >=0)) {
angleDiff += 180;
}
wheel.setRotation(startRotation - angleDiff);
}
};
}, false);
}
function getRandomReward() {
var mainDigit = Math.round(Math.random() * 9);
return mainDigit + '\n0\n0';
}
function addWedge(n) {
var s = getRandomColor();
var reward = getRandomReward();
var r = s[0];
var g = s[1];
var b = s[2];
var angle = 360 / NUM_WEDGES;
var endColor = 'rgb(' + r + ',' + g + ',' + b + ')';
r += 100;
g += 100;
b += 100;
var startColor = 'rgb(' + r + ',' + g + ',' + b + ')';
var wedge = new Kinetic.Group({
rotation: n * 360 / NUM_WEDGES,
});
var wedgeBackground = new Kinetic.Wedge({
radius: WHEEL_RADIUS,
angle: angle,
fillRadialGradientStartRadius: 0,
fillRadialGradientEndRadius: WHEEL_RADIUS,
fillRadialGradientColorStops: [0, startColor, 1, endColor],
fill: '#64e9f8',
fillPriority: 'radial-gradient',
stroke: '#ccc',
strokeWidth: 2,
rotation: (90 + angle/2) * -1
});
wedge.add(wedgeBackground);
var text = new Kinetic.Text({
text: reward,
fontFamily: 'Calibri',
fontSize: 50,
fill: 'white',
align: 'center',
stroke: 'yellow',
strokeWidth: 1,
listening: false
});
text.offsetX(text.width()/2);
text.offsetY(WHEEL_RADIUS - 15);
wedge.add(text);
volume = createVolumeControl(angle, endColor);
wedge.add(volume);
wheel.add(wedge);
}
var activeWedge;
function createVolumeControl(angle, colour){
var volume = new Kinetic.Wedge({
radius: 100,
angle: angle,
fill: colour,
stroke: '#000000',
rotation: (90 + angle/2) * -1
});
volume.on("mousedown touchstart", function() {
currentVolume = this;
action='increase';
});
return volume;
}
function animate(frame) {
// wheel
var angularVelocityChange = angularVelocity * frame.timeDiff * (1 - ANGULAR_FRICTION) / 1000;
angularVelocity -= angularVelocityChange;
if(controlled) {
angularVelocity = ((wheel.getRotation() - lastRotation) * 1000 / frame.timeDiff);
}
else {
wheel.rotate(frame.timeDiff * angularVelocity / 1000);
}
lastRotation = wheel.getRotation();
// pointer
var intersectedWedge = layer.getIntersection({x: stage.width()/2, y: 50});
if (intersectedWedge && (!activeWedge || activeWedge._id !== intersectedWedge._id)) {
pointerTween.reset();
pointerTween.play();
activeWedge = intersectedWedge;
}
}
function init() {
stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 500
});
layer = new Kinetic.Layer();
wheel = new Kinetic.Group({
x: stage.getWidth() / 2,
y: WHEEL_RADIUS + 20
});
for(var n = 0; n < NUM_WEDGES; n++) {
addWedge(n);
}
pointer = new Kinetic.Wedge({
fillRadialGradientStartPoint: 0,
fillRadialGradientStartRadius: 0,
fillRadialGradientEndPoint: 0,
fillRadialGradientEndRadius: 30,
fillRadialGradientColorStops: [0, 'white', 1, 'red'],
stroke: 'white',
strokeWidth: 2,
lineJoin: 'round',
angle: 30,
radius: 30,
x: stage.getWidth() / 2,
y: 20,
rotation: -105,
shadowColor: 'black',
shadowOffset: {x:3,y:3},
shadowBlur: 2,
shadowOpacity: 0.5
});
// add components to the stage
layer.add(wheel);
layer.add(pointer);
stage.add(layer);
pointerTween = new Kinetic.Tween({
node: pointer,
duration: 0.1,
easing: Kinetic.Easings.EaseInOut,
y: 30
});
pointerTween.finish();
var radiusPlus2 = WHEEL_RADIUS + 2;
wheel.cache({
x: -1* radiusPlus2,
y: -1* radiusPlus2,
width: radiusPlus2 * 2,
height: radiusPlus2 * 2
}).offset({
x: radiusPlus2,
y: radiusPlus2
});
layer.draw();
// bind events
bind();
var anim = new Kinetic.Animation(animate, layer);
//document.getElementById('debug').appendChild(layer.hitCanvas._canvas);
// wait one second and then spin the wheel
setTimeout(function() {
anim.start();
}, 1000);
}
init();
I made a couple of changes to the script which greatly improved the response time. The first was replacing layer.draw() with layer.batchDraw(). As the draw function was being called on each touchmove event it was making the interaction clunky. BatchDraw on the other hand will stack up draw requests internally "limit the number of redraws per second based on the maximum number of frames per second" (http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-batch-draw).
The jumping around of the canvas I seeing originally when I cached/cleared the wheel was due to the fact that I wasn't resetting the offset on the wheel when I cleared the cache.
http://jsfiddle.net/leydar/a7tkA/5
wheel.clearCache().offset({
x: 0,
y: 0
});
I hope this is of benefit to someone else. It's still not perfectly responsive but it's at least going in the right direction.
Stephen

Adding grid over Fabric.js canvas

I just started using Fabric.js (I have to say, I'm impressed).
I want to add a grid over the fabric objects. In the following code, I am putting my grid canvas right over on the Fabric canvas. The problem here is that, I now cannot move my fabric objects!
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script type='text/javascript' src='http://code.jquery.com/jquery-1.7.1.js'></script>
<script type='text/javascript' src='http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.2.0/fabric.all.min.js'></script>
</head>
<body>
<div style="height:480px;width:640px;border:1px solid #ccc;position:relative;font:16px/26px Georgia, Garamond, Serif;overflow:auto;">
<canvas id="rubber" width="800" height="800"
style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
<canvas id="myCanvas" width="800" height="800"
style="position: absolute; left: 0; top: 0; z-index: 1;"></canvas>
</div>
<script>
//<![CDATA[
$(window).load(function(){
$(document).ready(function () {
function renderGrid(x_size,y_size, color)
{
var canvas = $("#myCanvas").get(0);
var context = canvas.getContext("2d");
context.save();
context.lineWidth = 0.5;
context.strokeStyle = color;
// horizontal grid lines
for(var i = 0; i <= canvas.height; i = i + x_size)
{
context.beginPath();
context.moveTo(0, i);
context.lineTo(canvas.width, i);
context.closePath();
context.stroke();
}
// vertical grid lines
for(var j = 0; j <= canvas.width; j = j + y_size)
{
context.beginPath();
context.moveTo(j, 0);
context.lineTo(j, canvas.height);
context.closePath();
context.stroke();
}
context.restore();
}
renderGrid(10,15, "gray");
});
});//]]>
var canvas = new fabric.Canvas('rubber');
canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 }));
canvas.selectionColor = 'rgba(0,255,0,0.3)';
canvas.selectionBorderColor = 'red';
canvas.selectionLineWidth = 5;
</script>
</body>
</html>
I am hoping that there is a way to do this in Fabric itself.
Any help would be awesome, thanks!
This two lines of code will work:
var gridsize = 5;
for(var x=1;x<(canvas.width/gridsize);x++)
{
canvas.add(new fabric.Line([100*x, 0, 100*x, 600],{ stroke: "#000000", strokeWidth: 1, selectable:false, strokeDashArray: [5, 5]}));
canvas.add(new fabric.Line([0, 100*x, 600, 100*x],{ stroke: "#000000", strokeWidth: 1, selectable:false, strokeDashArray: [5, 5]}));
}
A shorter version and more generic for copy/paste :
var oCanvas; // must be your canvas object
var gridWidth; // <= you must define this with final grid width
var gridHeight; // <= you must define this with final grid height
// to manipulate grid after creation
var oGridGroup = new fabric.Group([], {left: 0, top: 0});
var gridSize = 20; // define grid size
// define presentation option of grid
var lineOption = {stroke: 'rgba(0,0,0,.4)', strokeWidth: 1, selectable:false, strokeDashArray: [3, 3]};
// do in two steps to limit the calculations
// first loop for vertical line
for(var i = Math.ceil(gridWidth/gridSize); i--;){
oGridGroup.add( new fabric.Line([gridSize*i, 0, gridSize*i, gridHeight], lineOption) );
}
// second loop for horizontal line
for(var i = Math.ceil(gridHeight/gridSize); i--;){
oGridGroup.add( new fabric.Line([0, gridSize*i, gridWidth, gridSize*i], lineOption) );
}
// Group add to canvas
oCanvas.add(oGridGroup);
I hope this will help you----
function draw_grid(grid_size) {
grid_size || (grid_size = 25);
currentCanvasWidth = canvas.getWidth();
currentcanvasHeight = canvas.getHeight();
// Drawing vertical lines
var x;
for (x = 0; x <= currentCanvasWidth; x += grid_size) {
this.grid_context.moveTo(x + 0.5, 0);
this.grid_context.lineTo(x + 0.5, currentCanvasHeight);
}
// Drawing horizontal lines
var y;
for (y = 0; y <= currentCanvasHeight; y += grid_size) {
this.grid_context.moveTo(0, y + 0.5);
this.grid_context.lineTo(currentCanvasWidth, y + 0.5);
}
grid_size = grid_size;
this.grid_context.strokeStyle = "black";
this.grid_context.stroke();
}
My solution is -
var width = canvas.width;
var height = canvas.height;
var j = 0;
var line = null;
var rect = [];
var size = 20;
console.log(width + ":" + height);
for (var i = 0; i < Math.ceil(width / 20); ++i) {
rect[0] = i * size;
rect[1] = 0;
rect[2] = i * size;
rect[3] = height;
line = null;
line = new fabric.Line(rect, {
stroke: '#999',
opacity: 0.5,
});
line.selectable = false;
canvas.add(line);
line.sendToBack();
}
for (i = 0; i < Math.ceil(height / 20); ++i) {
rect[0] = 0;
rect[1] = i * size;
rect[2] = width;
rect[3] = i * size;
line = null;
line = new fabric.Line(rect, {
stroke: '#999',
opacity: 0.5,
});
line.selectable = false;
canvas.add(line);
line.sendToBack();
}
canvas.renderAll();
You have to save all line objects for removing grid, or you can added all line objects to a group, and you can remove the group for removing grid, well I think this is not elegant one, but worked.
If you don't insist on generating your grid dynamically you might want to consider the native overlay image function that fabric.js provides.
var canvas = new fabric.Canvas('rubber');
canvas.setOverlayImage('grid.png', canvas.renderAll.bind(canvas));
It won't hinder interactions with the objects on the canvas at all.
I really liked #Draeli's answer, but it seemed it wasn't working with the latest fabric version. Fixed the old one and added one slight adjustment that was necessary for myself - centring the grid.
Anyhow, maybe someone else finds it useful:
const gridSize = 100;
const width = this.canvas.getWidth();
const height = this.canvas.getHeight();
const left = (width % gridSize) / 2;
const top = (height % gridSize) / 2;
const lines = [];
const lineOption = {stroke: 'rgba(0,0,0,1)', strokeWidth: 1, selectable: false};
for (let i = Math.ceil(width / gridSize); i--;) {
lines.push(new fabric.Line([gridSize * i, -top, gridSize * i, height], lineOption));
}
for (let i = Math.ceil(height / gridSize); i--;) {
lines.push(new fabric.Line([-left, gridSize * i, width, gridSize * i], lineOption));
}
const oGridGroup = new fabric.Group(lines, {left: 0, top: 0});
this.canvas.add(oGridGroup);
this.canvas - this supposedly is the fabric instance.
Here is my solution, works with Fabric Js 4.
at the end there is 2 events listeners that append the grid when object moving and remove the grid when object move end. (improvement of #draeli answer https://stackoverflow.com/a/35936606/1727357 )
(function () {
const gcd = (a, b) => {
if (!b) {
return a;
}
return gcd(b, a % b);
}
const gridWidth = canvas.getWidth();
const gridHeight = canvas.getHeight();
const oGridGroup = [];
console.log(gcd(gridWidth, gridHeight));
const gridRows = gcd(gridWidth, gridHeight);
const gridCols = gcd(gridWidth, gridHeight);
const lineOption = { stroke: 'rgba(0,0,0,.1)', strokeWidth: 1, selectable: false, evented: false };
for (let i = 0; i <= gridWidth; i += gridCols) {
oGridGroup.push(new fabric.Line([i, 0, i, gridHeight], lineOption));
}
for (let i = 0; i <= gridHeight; i += gridRows) {
oGridGroup.push(new fabric.Line([0, i, gridWidth, i], lineOption));
}
const theGorup = new fabric.Group(oGridGroup);
theGorup.set({
selectable: false,
evented: false
})
canvas.on('mouse:down', function (event) {
if (event.target) {
canvas.add(theGorup);
}
});
canvas.on('mouse:up', function (event) {
canvas.remove(theGorup);
});
}())
Updated:
Answer is based on the code posted by rafi:
I have updated the missing grid_context.
Kindly replace "<your canvas Id>" with your canvas Id in string. For e.g. "myCanvas".
Usually, any operation you do on the canvas will clear out grid on the canvas, register the after:render event on fabric.js canvas to redraw it. So you can always see it.
var canvas = new fabric.Canvas(<your canvas Id>);
canvas.on('after:render',function(ctx){
draw_grid(25);
});
function draw_grid(grid_size) {
grid_size || (grid_size = 25);
var currentCanvas = document.getElementById(<your canvas Id>);
var grid_context = currentCanvas.getContext("2d");
var currentCanvasWidth = canvas.getWidth();
var currentCanvasHeight = canvas.getHeight();
// Drawing vertical lines
var x;
for (x = 0; x <= currentCanvasWidth; x += grid_size) {
grid_context.moveTo(x + 0.5, 0);
grid_context.lineTo(x + 0.5, currentCanvasHeight);
}
// Drawing horizontal lines
var y;
for (y = 0; y <= currentCanvasHeight; y += grid_size) {
grid_context.moveTo(0, y + 0.5);
grid_context.lineTo(currentCanvasWidth, y + 0.5);
}
grid_context.strokeStyle = "#0000002b";
grid_context.stroke();
}

KineticJS .on() not working on cloned shape

I'm making an editable pattern that users can use and reuse to make a larger pattern.
When users drag the first small pattern into the large pattern grid, I clone it, add it to another layer and let users reuse or edit it.
The cloned node is still editable and draggable, but the bound events are not firing with interaction.
Here's my code:
var B = A = new Kinetic.Shape();
var stage = new Kinetic.Stage({
container: 'container',
width: 1000,
height: 650
});
var Alayer = new Kinetic.Layer();
var Blayer = new Kinetic.Layer();
var BGrid = new Kinetic.Group();
for(var v = 0; v < 4; v++){
for(var h = 0; h < 4; h++){
(function(){
var grid = new Kinetic.Rect({
x: 300 + (h * 120),
y: 50 + (v *120),
width: 120,
height: 120,
stroke: 'black',
strokeWidth: 1,
listening: false
});
BGrid.add(grid);
})();
}
}
(function() {
var AS = new Kinetic.Rect({
x: 150,
y: 110,
width: 120,
height: 120,
draggable:true,
stroke: 'black',
strokeWidth: 1,
offset: [60,60],
});
B = AS;
Alayer.add(B);
})();
Blayer.add(BGrid);
stage.add(Blayer);
stage.add(Alayer);
Blayer.on('click', function(evt) {
B = evt.targetNode;
B.setStroke('red');
});
B.on('dragend',function(){
var px = B.getX();
var py = B.getY();
//some code that's not executing
});
A.on('dragend',function(){
var sx = A.getX();
var sy = A.getY();
if((300 < sx && sx < 780) && (50 < sy && sy < 530)){
A.moveTo(Blayer);
B = A;
var C = A.clone();
C.setPosition(150,110);
Alayer.add(C);
A = C;
}else{
A.setPosition(150,110);
}
Alayer.draw();
Blayer.draw();
});
I'd really appreciate any help with this.
You have typo:
B.on('dragend',function(){
var px = B.getX();
var py = B.getY(); // not b.getY();
alert("dragend!");
//some code that's executing
});
http://jsfiddle.net/lavrton/JYqJp/
Here's the solution I came up with, not sure if it'll help anyone, but I hope it does.
stage.on('dragend',function(e){
var t = e.targetNode;
var n = t.getName();
var sx = t.getAbsolutePosition().x;
if(300 < sx && sx < 780 && n == 'A'){
//previous code for A
}else if(300 < sx && sx < 780 && n != 'A'){
//previous code for B
}else if(n == 'A'){
t.setAbsolutePosition(150,110);
}else{
t.remove();
}
stage.draw();
});`

Categories

Resources