i am new in fabric js want to set the drag limit
i have also try with https://github.com/kangax/fabric.js/wiki/Working-with-events
not able to get the solution.
please check the attached image, object can move anyware but it should be display in red area only.i want this. help me...thanks in advance !!
While Orangepill's answer is correct, it produces a "stuttering" when your object hits the object bounds. If you have a rectangular bounding box (and not a complex bounding object) an alternative is to allow the object to be dragged along the bounds and "slide" along the bounding box. You do this by capping the coordinates values and letting the other dimension move as usual. An example snippet would look like so:
var canvas = new fabric.Canvas("bounded");
var boundingBox = new fabric.Rect({
fill: "none",
width: 600,
height: 400,
hasBorders: false,
hasControls: false,
lockMovementX: true,
lockMovementY: true,
evented: false,
stroke: "red"
});
var movingBox = new fabric.Rect({
width: 100,
height: 100,
hasBorders: false,
hasControls: false
});
canvas.on("object:moving", function() {
var top = movingBox.top;
var bottom = top + movingBox.height;
var left = movingBox.left;
var right = left + movingBox.width;
var topBound = boundingBox.top;
var bottomBound = topBound + boundingBox.height;
var leftBound = boundingBox.left;
var rightBound = leftBound + boundingBox.width;
// capping logic here
movingBox.setLeft(Math.min(Math.max(left, leftBound), rightBound - movingBox.width));
movingBox.setTop(Math.min(Math.max(top, topBound), bottomBound - movingBox.height));
});
canvas.add(boundingBox);
canvas.add(movingBox);
See this example in JSFiddle here
Felix Fung's answer was a starting point, but there are many things to consider. Here is a version that accounts for some of them.
It handles the canvas having a viewport transform (ie, zoomed/panned) and objects that are center-origined instead of left/top-origined. It also constrains objects wider/taller than the viewport to the top/left instead of the bottom/right.
canvas.on("object:moving", function(e) {
var obj = e.target;
var canvas = obj.canvas;
var top = obj.top;
var left = obj.left;
var zoom = canvas.getZoom();
var pan_x = canvas.viewportTransform[4];
var pan_y = canvas.viewportTransform[5];
// width & height we are constraining to must be calculated by applying the inverse of the current viewportTransform
var c_width = canvas.width / zoom;
var c_height = canvas.height / zoom;
var w = obj.width * obj.scaleX
var left_adjust, right_adjust
if(obj.originX == "center") {
left_adjust = right_adjust = w / 2;
} else {
left_adjust = 0;
right_adjust = w;
}
var h = obj.height * obj.scaleY;
var top_adjust, bottom_adjust;
if(obj.originY == "center") {
top_adjust = bottom_adjust = h / 2;
} else {
top_adjust = 0;
bottom_adjust = h;
}
// if you need margins set them here
var top_margin = 0;
var bottom_margin = 0;
var left_margin = 0;
var right_margin = 0;
var top_bound = top_margin + top_adjust - pan_y;
var bottom_bound = c_height - bottom_adjust - bottom_margin - pan_y;
var left_bound = left_margin + left_adjust - pan_x;
var right_bound = c_width - right_adjust - right_margin - pan_x;
if( w > c_width ) {
obj.setLeft(left_bound);
} else {
obj.setLeft(Math.min(Math.max(left, left_bound), right_bound));
}
if( h > c_height ) {
obj.setTop(top_bound);
} else {
obj.setTop(Math.min(Math.max(top, top_bound), bottom_bound));
}
});
What had worked for me is to create an event listener for the object:moving event. When the move is happening you update the goodtop and goodleft variables and once you are out of bounds to reposition the object to the last good points.
var goodtop, goodleft, boundingObject;
canvas.on("object:moving", function(){
var obj = this.relatedTarget;
var bounds = boundingObject;
obj.setCoords();
if(!obj.isContainedWithinObject(bounds)){
obj.setTop(goodtop);
obj.setLeft(goodleft);
canvas.refresh();
} else {
goodtop = obj.top;
goodleft = obj.left;
}
});
I used Michael Johnston's snipped as a starting point to add bounding control for rotated elements. This snipped only covers the cases when either (obj.centerX && obj.centerY == "center") || (obj.centerX && obj.centerY != "center")
canvasRef.current.on("object:moving", function (e) {
var obj = e.target;
var canvas = obj.canvas;
var zoom = canvas.getZoom();
var pan_x = canvas.viewportTransform[4];
var pan_y = canvas.viewportTransform[5];
// get left, top, width, and height of object
var left = obj.left;
var top = obj.top;
var width = obj.width * obj.scaleX;
var height = obj.height * obj.scaleY;
// width & height we are constraining to must be calculated by applying the inverse of the current viewportTransform
var c_width = canvas.width / zoom;
var c_height = canvas.height / zoom;
// calculate values that define the origin of the object, when it is centered in the center or not
var left_adjust, right_adjust;
if (obj.originX == "center") {
left_adjust = right_adjust = width / 2;
} else {
left_adjust = 0;
right_adjust = width;
}
var top_adjust, bottom_adjust;
if (obj.originY == "center") {
top_adjust = bottom_adjust = height / 2;
} else {
top_adjust = 0;
bottom_adjust = height;
}
// support for rotated objects
if (obj.angle) {
var angle = obj.angle;
if (angle > 270) {
angle -= 270;
} else if (angle > 180) {
angle -= 180;
} else if (angle > 90) {
angle -= 90;
}
const radians = angle * (Math.PI / 180);
const w_opposite = width * Math.sin(radians);
const w_adjacent = width * Math.cos(radians);
const h_opposite = height * Math.sin(radians);
const h_adjacent = height * Math.cos(radians);
if (obj.originX != "center" && obj.originY != "center") {
if (obj.angle <= 90) {
left_adjust = h_opposite;
top_adjust = 0;
right_adjust = w_adjacent;
bottom_adjust = h_adjacent + w_opposite;
} else if (obj.angle > 90 && obj.angle <= 180) {
left_adjust = h_adjacent + w_opposite;
top_adjust = h_opposite;
right_adjust = 0;
bottom_adjust = w_adjacent;
} else if (obj.angle > 180 && obj.angle <= 270) {
left_adjust = w_adjacent;
top_adjust = w_opposite + h_adjacent;
right_adjust = h_opposite;
bottom_adjust = 0;
} else {
left_adjust = 0;
top_adjust = w_adjacent;
right_adjust = w_opposite + h_adjacent;
bottom_adjust = h_opposite;
}
}
if (obj.originX == "center" && obj.originY == "center") {
if (obj.angle <= 90 || (obj.angle > 180 && obj.angle <= 270)) {
left_adjust = (w_adjacent + h_opposite) / 2;
right_adjust = (w_adjacent + h_opposite) / 2;
top_adjust = (h_adjacent + w_opposite) / 2;
bottom_adjust = (h_adjacent + w_opposite) / 2;
} else {
left_adjust = (h_adjacent + w_opposite) / 2;
right_adjust = (h_adjacent + w_opposite) / 2;
top_adjust = (w_adjacent + h_opposite) / 2;
bottom_adjust = (w_adjacent + h_opposite) / 2;
}
}
}
// if you need margins set them here
var top_margin = 0;
var bottom_margin = 0;
var left_margin = 0;
var right_margin = 0;
var top_bound = top_margin + top_adjust - pan_y;
var bottom_bound = c_height - bottom_adjust - bottom_margin - pan_y;
var left_bound = left_margin + left_adjust - pan_x;
var right_bound = c_width - right_adjust - right_margin - pan_x;
if (width > c_width) {
obj.set("left", left_bound);
} else {
obj.set("left", Math.min(Math.max(left, left_bound), right_bound));
}
if (height > c_height) {
obj.set("top", top_bound);
} else {
obj.set("top", Math.min(Math.max(top, top_bound), bottom_bound));
}
});
SEE THE WORKING EXAMPLE
It's as simple as a water try this
just use this js
<script type="text/javascript">
//use global variable for canvas object
var canvas;
var ctx;
function onLoad() {
//get fabric canvas with id mycanvas
canvas = new fabric.Canvas('mycanvas');
canvas.on("mouse : down",function{
//get canvas 2d context
ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.rect(115,60,221,390);//specify bounded rectangle
ctx.closePath();
ctx.clip();
ctx.save();
});
//now restore the context on mouse up
canvas.on("mouse : up",function(){
ctx.restore();//restore the context
});
}
</script>
Hope this will help you.
Njoy coding :)
now u can have the same clipping region for every object supported by Fabric.js for transformation and moving.
try it :).
Related
I recreated the scrolling text box tutorial in my game. However, it is running a bit glitchy on mobile. For example, if I swipe up, the text first goes down for a second and then follows my finger up. You’ll see the problem if you open the tutorial on mobile. Any thoughts? I copied my code below.
var graphics = scene.make.graphics();
graphics.fillRect(x, y + 10, width, height - 20);
var mask = new Phaser.Display.Masks.GeometryMask(scene, graphics);
var text = scene.add.text(x + 20, y + 20, content, {
fontFamily: 'Assistant',
fontSize: '28px',
color: '#000000',
wordWrap: { width: width - 20 }
}).setOrigin(0);
text.setMask(mask);
var minY = height - text.height - 20;
if (text.height <= height - 20) {
minY = y + 20;
}
// The rectangle they can 'drag' within
var zone = scene.add.zone(x, y - 3, width, height + 6).setOrigin(0).setInteractive({useHandCursor: true});
zone.on('pointermove', function (pointer) {
if (pointer.isDown) {
text.y += (pointer.velocity.y / 10);
text.y = Phaser.Math.Clamp(text.y, minY, y + 20);
}
});
I had the same issue. My solution is using "pointer.y" instead of "pointer.velocity.y".
Here is my code:
var previousPointerPositionY;
var currentPointerPositionY;
zone.on('pointerdown', function (pointer) {
previousPointerPositionY = pointer.y;
});
zone.on('pointermove', function (pointer) {
if (pointer.isDown) {
currentPointerPositionY = pointer.y;
if(currentPointerPositionY > previousPointerPositionY){
text.y += 5;
} else if(currentPointerPositionY < previousPointerPositionY){
text.y -= 5;
}
previousPointerPositionY = currentPointerPositionY;
text.y = Phaser.Math.Clamp(text.y, -360, 150);
}
});
I want to display an hexagonal grid where each cell displays its coordinate in the system (thus the top leftmost cell will have a1 written in it, the one immediately right to it will have b1 written in it, etc).
The code below is meant to achieve this (and nearly does it).
However all the texts are crammed in one small place (even though I specified the location on line 48 of the code, and this location seems correct since the rest of the image is fine). What am I doing wrong ?
<html>
<head>
<meta charset="utf-8">
<title>Hex board</title>
<script type="text/javascript">
var r = 20;
var w = r*2*(Math.sqrt(3)/2);
var ctx;
var mainWidth = 850;
var mainHeight = 600;
var dim = 11;
var i,x,y, txt;
var alphabet =["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"];
function textFromCoordinates(x,y)
{
return(alphabet[x]+(y+1));
}
function drawHexagon(c, x, y, r)
{
c.beginPath();
c.moveTo(x, y-r);
for(i=0; i<6; i++) {
c.lineTo(x+r*Math.cos(Math.PI*(1.5+1/3*i)), y+r*Math.sin(Math.PI*(1.5+1/3*i)));
}
c.closePath();
c.fill();
c.stroke();
}
function draw()
{
ctx.clearRect(0, 0, mainWidth, mainHeight);
ctx.lineWidth = 1;
ctx.strokeStyle = "white";
for(y=0; y<dim; y++)
{
for(x=0; x<dim; x++)
{
ctx.fillStyle = "rgb(" + (x+241) + "," + (y+220) + ",178)";
drawHexagon(ctx, (x+y)*w - (y-4)*(w/2), (y+2)*1.5*r, r);
txt = textFromCoordinates(x,y);
ctx.font = 'italic 40pt Calibri';
ctx.fillStyle = "black";
ctx.moveTo((x+y)*w - (y-4)*(w/2), (y+2)*1.5*r);
ctx.fillText(txt,mainWidth/dim,mainHeight/dim);
}
}
}
function load()
{
var canvas = document.getElementById("output");
ctx = canvas.getContext("2d");
draw();
}
</script>
</head>
<body onload="load()">
<canvas style="position:absolute,top:0px,left:20px" width="850" height="600" id="output">Canvas not supported...</canvas>
</body>
</html>
You need to draw the text at the same coordinates as the hexagon:
ctx.fillText(txt,(x+y)*w - (y-4)*(w/2),(y+2)*1.5*r);
Also I've changed the font to a smaller size, and I'm aligning it around the center:
ctx.font = 'italic 16px Calibri';
ctx.textAlign = "center";
ctx.textBaseline = "middle";
I hope this is what you need.
var r = 20;
var w = r*2*(Math.sqrt(3)/2);
var ctx;
var mainWidth = 850;
var mainHeight = 600;
var dim = 11;
var i,x,y, txt;
var alphabet =["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"];
function textFromCoordinates(x,y)
{
return(alphabet[x]+(y+1));
}
function drawHexagon(c, x, y, r)
{
c.beginPath();
c.moveTo(x, y-r);
for(i=0; i<6; i++) {
c.lineTo(x+r*Math.cos(Math.PI*(1.5+1/3*i)), y+r*Math.sin(Math.PI*(1.5+1/3*i)));
}
c.closePath();
c.fill();
c.stroke();
}
function draw()
{
ctx.clearRect(0, 0, mainWidth, mainHeight);
ctx.lineWidth = 1;
ctx.strokeStyle = "white";
for(y=0; y<dim; y++)
{
for(x=0; x<dim; x++)
{
ctx.fillStyle = "rgb(" + (x+241) + "," + (y+220) + ",178)";
drawHexagon(ctx, (x+y)*w - (y-4)*(w/2), (y+2)*1.5*r, r);
txt = textFromCoordinates(x,y);
ctx.font = 'italic 16px Calibri';
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "black";
ctx.fillText(txt,(x+y)*w - (y-4)*(w/2),(y+2)*1.5*r);
}
}
}
function load()
{
var canvas = document.getElementById("output");
ctx = canvas.getContext("2d");
draw();
}
load()
canvas{border:1px solid}
<canvas style="position:absolute,top:0px,left:20px" width="850" height="600" id="output">Canvas not supported...</canvas>
Two questions:
1) Why is it so hard to find an example where a 3D object is freely rotating according to mouse movement, for example, if I were to do the following for an object:
object.rotation.x += 1;
object.rotation.y += 1;
The object will not always rotate at the same degree, this seems to be a mathematical problem, does someone have an example or a mathematical explaination for how rotation works, I believe there's something that needs to be implemented for the change in the center of geometry.
When an object has more than 2 center of geometries, for example in the case of an object with different morph targets, why is there only one bounding box, is there an easy way to find the center of geometry for the bounding box at each morph target instead of just an average center of geometry?
Not sure what you're going for, but is it something like this:
https://jsfiddle.net/72p3z0Lh/27/
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var screenX = 0;
var screenY = 0;
var deltaX = 0;
var deltaY = 0;
renderer.domElement.addEventListener('mousemove', function(event) {
deltaX = event.screenX - screenX;
deltaY = event.screenY - screenY;
screenX = event.screenX;
screenY = event.screenY;
});
var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({
color: 0x00ff00
});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.x = 5;
camera.position.y= 2;
camera.lookAt(new THREE.Vector3(0,0,0));
cube.geometry.translate( -.5, 0, 0 ); //Center Offset
var render = function() {
requestAnimationFrame(render);
cube.rotateOnAxis(cube.worldToLocal(camera.up.clone()).normalize(), deltaX / 20);
cube.rotateOnAxis(cube.worldToLocal(camera.getWorldDirection().clone().cross(camera.up)).normalize(), deltaY / 20);
deltaX = 0;
deltaY = 0;
renderer.render(scene, camera);
};
render();
How to apply gradient colors on 5 concentric circles using html5 canvas
gradient = context.createRadialGradient(startx, starty,radAvg, xEnd, yEnd,radAvg);
gradient.addColorStop(0, startColor);
gradient.addColorStop(1.0, endColor);
Current results:
JavaScript code:
var colors = ["#B8D430", "#3AB745", "#029990", "#3501CB",
"#2E2C75", "#673A7E", "#CC0071", "#F80120",
"#F35B20", "#FB9A00", "#FFCC00", "#FEF200"];
var restaraunts = ["Wendy's", "McDonalds", "Chick-fil-a", "Five Guys",
"Gold Star", "La Mexicana", "Chipotle", "Tazza Mia",
"Panera", "Just Crepes", "Arby's", "Indian"];
var startAngle = 0;
var arc = Math.PI / 6;
var spinTimeout = null;
var spinArcStart = 10;
var spinTime = 0;
var spinTimeTotal = 0;
var ctx;
function draw() {
drawRouletteWheel();
}
function drawRouletteWheel() {
var canvas = document.getElementById("wheelcanvas");
if (canvas.getContext) {
var outsideRadius = 200;
var textRadius = 160;
var insideRadius = 125;
ctx = canvas.getContext("2d");
ctx.clearRect(0,0,500,500);
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
ctx.font = 'bold 12px sans-serif';
for(var i = 0; i < 12; i++) {
var angle = startAngle + i * arc;
ctx.fillStyle = colors[i];
ctx.beginPath();
ctx.arc(250, 250, outsideRadius, angle, angle + arc, false);
ctx.arc(250, 250, insideRadius, angle + arc, angle, true);
ctx.stroke();
ctx.fill();
ctx.save();
ctx.shadowOffsetX = -1;
ctx.shadowOffsetY = -1;
ctx.shadowBlur = 0;
ctx.shadowColor = "rgb(220,220,220)";
// ctx.fillStyle = "black";
ctx.translate(250 + Math.cos(angle + arc / 2) * textRadius, 250 + Math.sin(angle + arc / 2) * textRadius);
ctx.rotate(angle + arc / 2 + Math.PI / 2);
var text = restaraunts[i];
ctx.fillText(text, -ctx.measureText(text).width / 2, 0);
ctx.restore();
}
//Arrow
ctx.fillStyle = "black";
ctx.beginPath();
ctx.moveTo(250 - 4, 250 - (outsideRadius + 5));
ctx.lineTo(250 + 4, 250 - (outsideRadius + 5));
ctx.lineTo(250 + 4, 250 - (outsideRadius - 5));
ctx.lineTo(250 + 9, 250 - (outsideRadius - 5));
ctx.lineTo(250 + 0, 250 - (outsideRadius - 13));
ctx.lineTo(250 - 9, 250 - (outsideRadius - 5));
ctx.lineTo(250 - 4, 250 - (outsideRadius - 5));
ctx.lineTo(250 - 4, 250 - (outsideRadius + 5));
ctx.fill();
}
}
function spin() {
spinAngleStart = Math.random() * 10 + 10;
spinTime = 0;
spinTimeTotal = Math.random() * 3 + 4 * 1000;
rotateWheel();
}
function rotateWheel() {
spinTime += 30;
if(spinTime >= spinTimeTotal) {
stopRotateWheel();
return;
}
var spinAngle = spinAngleStart - easeOut(spinTime, 0, spinAngleStart, spinTimeTotal);
startAngle += (spinAngle * Math.PI / 180);
drawRouletteWheel();
spinTimeout = setTimeout('rotateWheel()', 30);
}
function stopRotateWheel() {
clearTimeout(spinTimeout);
var degrees = startAngle * 180 / Math.PI + 90;
var arcd = arc * 180 / Math.PI;
var index = Math.floor((360 - degrees % 360) / arcd);
ctx.save();
ctx.font = 'bold 30px sans-serif';
var text = restaraunts[index]
ctx.fillText(text, 250 - ctx.measureText(text).width / 2, 250 + 10);
ctx.restore();
}
function easeOut(t, b, c, d) {
var ts = (t/=d)*t;
var tc = ts*t;
return b+c*(tc + -3*ts + 3*t);
}
draw();
here is my solution
var Mid_diff=(startAngle+endAngle)/2;
var dest_x=arc.center.x+(arc.maxRadius*Math.cos(Mid_diff));
var dest_y=arc.center.y+(arc.maxRadius*Math.sin(Mid_diff));
var gradient = context.createRadialGradient( arc.center.x,arc.center.y,0,dest_x,dest_y,arc.maxRadius);
gradient.addColoStop(0,rgba(0,12,107,0);
gradient.addColoStop(1,rgba(0,12,107,1);
context.strokeStyle = gradient;
worked
I've been trying out Three.js today and I've had trouble getting my Mesh to change according using mesh.rotation.x += 10; for example.
The code below renders a triangle and the camera moves around onMouseMove but inside the render() function, it fails to scale or rotate the Mesh obj.
Appreciate the pointers.
<body>
<div id="container" style="border: #0f0 solid;">
</div>
<script type="text/javascript">
var mouseX = 0, mouseY = 0;
windowHalfX = window.innerWidth / 2,
windowHalfY = window.innerHeight / 2;
// get the DOM element to attach to
var container = document.getElementById("container");
// create a WebGL renderer, camera
// and a scene
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
camera = new THREE.PerspectiveCamera( 25, window.innerWidth / window.innerHeight, 50, 1e7 );
var scene = new THREE.Scene();
var obj;
var geom = new THREE.Geometry();
var stats;
init();
animate();
function init(){
// the camera starts at 0,0,0 so pull it back
camera.position.z = 400;
// start the renderer
// attach the render-supplied DOM element
container.appendChild(renderer.domElement);
// create a new mesh with triangle geometry
// create the sphere's material
var material = new THREE.MeshLambertMaterial(
{
color: 0x00FF00
});
var v1 = new THREE.Vector3(50,0,0);
var v2 = new THREE.Vector3(50,100,0);
var v3 = new THREE.Vector3(0, 50, 0);
geom.vertices.push(new THREE.Vertex(v1));
geom.vertices.push(new THREE.Vertex(v2));
geom.vertices.push(new THREE.Vertex(v3));
geom.faces.push(new THREE.Face3(0,1,2));
geom.computeFaceNormals();
obj = new THREE.Mesh(geom, material);
obj.doubleSided = true;
obj.rotation.x = 0.1;
// add the obj to the scene
scene.addObject(obj);
// create a point light
var pointLight = new THREE.PointLight( 0xFFFFFF );
// set its position
pointLight.position.x = 0;
pointLight.position.y = 0;
pointLight.position.z = 600;
//pointLight.lookAt(obj);
// add to the scene
scene.add(pointLight);
ambientLight = new THREE.AmbientLight( 0xbbbbbb );
scene.add(ambientLight);
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
// draw!
renderer.render(scene, camera);
}
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function onDocumentMouseMove(event) {
mouseX = event.clientX - windowHalfX;
mouseY = event.clientY - windowHalfY;
}
function render() {
camera.position.x += ( mouseX - camera.position.x ) * .05;
camera.position.y += ( - mouseY + 200 - camera.position.y ) * .05;
camera.lookAt( scene.position );
var time = Date.now() * 0.0015;
for ( var i = 0; i < scene.objects.length; i ++ ) {
scene.objects[ i ].rotation.y = time * ( i % 2 ? 1 : -1 );
}
obj.rotation.x += 20;
renderer.clear();
renderer.render( scene, camera );
}
</script>
</body>
From r45 scene.addObject(obj); must be now scene.add(obj);
See Three.js commit log:
2011 10 06 - r45 - Object/Scene.add*() and Object/Scene.remove*() are now Object/Scene.add() and Object/Scene.remove()