Related
I am using FabricJS version : 3.6.3
I want to make new FabricJS class called : Button
So that I have extend one class called Textbox from fabric js, which will Draw a Rectangle behind Text and it looking like a button.
But Problem is that, I can't set height to that Button because height is not allow in Texbox object.
I want to set Height and Width to Button object. Width is working Properly due to Textbox. it will also warp Text if width keep smaller then text width, and can be editable by double clicking on it. But only problem is that can't set Height to an object
it should be Text vertically center when Height is increase.
In short I want to make this kind of functionality in fabric js using object customization.
Expected Output :
but Actual Output :
Here Is my Code That Create button :
// fabric js custom button class
(function (fabric) {
"use strict";
// var fabric = global.fabric || (global.fabric = {});
fabric.Button = fabric.util.createClass(fabric.Textbox, {
type: "button",
stateProperties: fabric.Object.prototype.stateProperties.concat(
"buttonRx",
"buttonRy",
"buttonFill",
"buttonPadding",
"buttonStrokeColor",
"buttonStrokeWidth"
),
buttonRx: 0,
buttonRy: 0,
buttonFill: "#ffffff00",
buttonPadding: 0,
buttonHeight: 0,
buttonWidth: 0,
textAlign: "center",
buttonStrokeColor: "#000000",
buttonStrokeWidth: 0,
_dimensionAffectingProps: fabric.Text.prototype._dimensionAffectingProps.concat(
"width",
"fontSize"
),
cacheProperties: fabric.Object.prototype.cacheProperties.concat(
"buttonRx",
"buttonRy",
"buttonFill",
"buttonPadding",
"buttonStrokeColor",
"buttonStrokeWidth"
),
initialize: function (text, options) {
this.text = text;
this.callSuper("initialize", text, options);
/* this.on("scaling", function () {
console.log('scaling', this.getScaledHeight());
this.set({
height: this.getScaledHeight(),
scaleY: 1,
});
}); */
this._initRxRy();
},
_initRxRy: function () {
if (this.buttonRx && !this.buttonRy) {
this.buttonRy = this.buttonRx;
} else if (this.buttonRy && !this.buttonRx) {
this.buttonRx = this.buttonRy;
}
},
/* _setCenter(){
}, */
_render: function (ctx) {
// 1x1 case (used in spray brush) optimization was removed because
// with caching and higher zoom level this makes more damage than help
// this.width = this.width * this.scaleX;
// this.height = this.height * this.scaleY;
// (this.scaleX = 1), (this.scaleY = 1);
var rx = this.buttonRx ? Math.min(this.buttonRx, this.width / 2) : 0,
ry = this.buttonRy ? Math.min(this.buttonRy, this.height / 2) : 0,
w = this.width + this.buttonPadding,
h = this.height + this.buttonPadding,
x = -this.width / 2 - this.buttonPadding / 2,
y = -this.height / 2 - this.buttonPadding / 2,
isRounded = rx !== 0 || ry !== 0,
/* "magic number" for bezier approximations of arcs (http://itc.ktu.lt/itc354/Riskus354.pdf) */
k = 1 - 0.5522847498;
ctx.beginPath();
ctx.moveTo(x + rx, y);
ctx.lineTo(x + w - rx, y);
isRounded &&
ctx.bezierCurveTo(x + w - k * rx, y, x + w, y + k * ry, x + w, y + ry);
ctx.lineTo(x + w, y + h - ry);
isRounded &&
ctx.bezierCurveTo(
x + w,
y + h - k * ry,
x + w - k * rx,
y + h,
x + w - rx,
y + h
);
ctx.lineTo(x + rx, y + h);
isRounded &&
ctx.bezierCurveTo(x + k * rx, y + h, x, y + h - k * ry, x, y + h - ry);
ctx.lineTo(x, y + ry);
isRounded && ctx.bezierCurveTo(x, y + k * ry, x + k * rx, y, x + rx, y);
ctx.closePath();
ctx.save();
if (this.buttonFill) {
ctx.fillStyle = this.buttonFill;
if (this.fillRule === "evenodd") {
ctx.fill("evenodd");
} else {
ctx.fill();
}
}
if (this.buttonStrokeWidth > 0) {
if (this.strokeUniform) {
ctx.scale(1 / this.scaleX, 1 / this.scaleY);
}
if (this.shadow && !this.shadow.affectStroke) {
this._removeShadow(ctx);
}
if (this.buttonStrokeColor) {
ctx.lineWidth = this.buttonStrokeWidth;
ctx.strokeStyle = this.buttonStrokeColor;
ctx.stroke();
} else {
ctx.lineWidth = this.buttonStrokeWidth;
ctx.stroke();
}
}
ctx.restore();
this.clearContextTop();
this._clearCache();
this.height = this.calcTextHeight();
this.saveState({ propertySet: "_dimensionAffectingProps" });
// this._renderPaintInOrder(ctx);
this._setTextStyles(ctx);
this._renderTextLinesBackground(ctx);
this._renderTextDecoration(ctx, "underline");
this._renderText(ctx);
this._renderTextDecoration(ctx, "overline");
this._renderTextDecoration(ctx, "linethrough");
this.initDimensions();
// this.callSuper('render', ctx);
},
toObject: function (propertiesToInclude) {
return this.callSuper(
"toObject",
[
"buttonRx",
"buttonRy",
"buttonFill",
"buttonPadding",
"buttonStrokeColor",
"buttonStrokeWidth",
"objectCaching",
].concat(propertiesToInclude)
);
},
});
fabric.Button.fromObject = function (object, callback) {
return fabric.Object._fromObject("Button", object, callback, "text");
};
})(fabric);
// fabric js class finish here
var canvas = [];
var cotainer = document.getElementById("canvas-container");
for (let i = 0; i < 1; i++) {
var width = 500,
height = 500;
var canvasEl = document.createElement("canvas");
canvasEl.id = "canvas-" + i;
cotainer.append(canvasEl);
var fabCanvas = new fabric.Canvas(canvasEl, {});
fabCanvas.setHeight(height);
fabCanvas.setWidth(width);
canvas.push(fabCanvas);
}
canvas.forEach((c) => {
var button = new fabric.Button("Click Me", {
text: "Click Me",
buttonStrokeColor: "#f00",
buttonStrokeWidth: 2,
width: 110,
fill: "#f00",
fontSize: 50,
width: 400,
buttonFill: "#42A5F5",
buttonRx: 15,
buttonRy: 15,
objectCaching: false,
fontFamily: "verdana",
});
c.add(button);
c.renderAll();
});
canvas{
border: 1px solid black
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.js"></script>
<div id="canvas-container">
</div>
The solution will be to set the scaleX and scaleY of the button text to 1 when you scale the Button Object and also set the font size of the text equal to its scale.
var tbox = new fabric.Button(v.textDisp, {
left: v.posX,
top: v.posY,
boxHeight: v.length // new create
});
fabric.Button = fabric.util.createClass(fabric.Textbox, {
type: "button",
stateProperties: fabric.Object.prototype.stateProperties.concat(
"buttonRx",
"buttonRy",
"buttonFill",
"buttonPadding",
"buttonStrokeColor",
"buttonStrokeWidth",
"boxHeight"
),
buttonRx: 0,
buttonRy: 0,
buttonPadding: 0,
buttonHeight: 0,
buttonWidth: 0,
buttonStrokeColor: "#000000",
buttonStrokeWidth: 0,
_dimensionAffectingProps: fabric.Text.prototype._dimensionAffectingProps.concat(
"width",
"fontSize"
),
cacheProperties: fabric.Object.prototype.cacheProperties.concat(
"buttonRx",
"buttonRy",
"buttonFill",
"buttonPadding",
"buttonStrokeColor",
"buttonStrokeWidth",
"boxHeight"
),
initialize: function (text, options) {
this.text = text;
this.callSuper("initialize", text, options);
/* this.on("scaling", function () {
console.log('scaling', this.getScaledHeight());
this.set({
height: this.getScaledHeight(),
scaleY: 1,
});
}); */
this._initRxRy();
},
_initRxRy: function () {
if (this.buttonRx && !this.buttonRy) {
this.buttonRy = this.buttonRx;
} else if (this.buttonRy && !this.buttonRx) {
this.buttonRx = this.buttonRy;
}
},
/* _setCenter(){
}, */
_render: function (ctx) {
// 1x1 case (used in spray brush) optimization was removed because
// with caching and higher zoom level this makes more damage than help
// this.width = this.width * this.scaleX;
// this.height = this.height * this.scaleY;
// (this.scaleX = 1), (this.scaleY = 1);
var rx = this.buttonRx ? Math.min(this.buttonRx, this.width / 2) : 0,
ry = this.buttonRy ? Math.min(this.buttonRy, this.height / 2) : 0,
w = this.width + this.buttonPadding,
h = this.height + this.buttonPadding,
x = -this.width / 2 - this.buttonPadding / 2,
y = -this.height / 2 - this.buttonPadding / 2,
hh = this.boxHeight * this.scaleY,
isRounded = rx !== 0 || ry !== 0,
k = 1 - 0.5522847498;
ctx.beginPath();
ctx.moveTo(x + rx, y);
ctx.lineTo(x + w - rx, y);
isRounded &&
ctx.bezierCurveTo(x + w - k * rx, y, x + w, y + k * ry, x + w, y + ry);
ctx.lineTo(x + w, y + hh - ry);
isRounded &&
ctx.bezierCurveTo(
x + w,
y + hh - k * ry,
x + w - k * rx,
y + hh,
x + w - rx,
y + hh
);
ctx.lineTo(x + rx, y + hh);
isRounded &&
ctx.bezierCurveTo(x + k * rx, y + hh, x, y + hh - k * ry, x, y + hh - ry);
ctx.lineTo(x, y + ry);
isRounded && ctx.bezierCurveTo(x, y + k * ry, x + k * rx, y, x + rx, y);
ctx.closePath();
ctx.save();
if (this.buttonFill) {
ctx.fillStyle = this.buttonFill;
if (this.fillRule === "evenodd") {
ctx.fill("evenodd");
} else {
ctx.fill();
}
}
if (this.buttonStrokeWidth > 0) {
if (this.strokeUniform) {
ctx.scale(1 / this.scaleX, 1 / this.scaleY);
}
if (this.shadow && !this.shadow.affectStroke) {
this._removeShadow(ctx);
}
if (this.buttonStrokeColor) {
ctx.lineWidth = this.buttonStrokeWidth;
ctx.strokeStyle = this.buttonStrokeColor;
ctx.stroke();
} else {
ctx.lineWidth = this.buttonStrokeWidth;
ctx.stroke();
}
}
ctx.restore();
this.clearContextTop();
this._clearCache();
this.height = this.calcTextHeight();
this.saveState({ propertySet: "_dimensionAffectingProps" });
// this._renderPaintInOrder(ctx);
this._setTextStyles(ctx);
this._renderTextLinesBackground(ctx);
this._renderTextDecoration(ctx, "underline");
this._renderText(ctx);
this._renderTextDecoration(ctx, "overline");
this._renderTextDecoration(ctx, "linethrough");
this.initDimensions();
// this.callSuper('render', ctx);
},
toObject: function (propertiesToInclude) {
return this.callSuper(
"toObject",
[
"buttonRx",
"buttonRy",
"buttonFill",
"buttonPadding",
"buttonStrokeColor",
"buttonStrokeWidth",
"objectCaching",
"boxHeight"
].concat(propertiesToInclude)
);
},
});
After adding boxHeight, declare a variable as 'hh' instead of'h' in "_render" When drawing, change to'hh' instead of'h'
I'm trying to huge graph visualization with threejs r86(latest master version), for showing 600,000 nodes I found a way to draw them faster than using mesh with THREE.points but know I need to make them draggable, after many searches I found raycast to found closest object to mouse point but I have a problem becouse all of taht points are just an object and can not be changed seperately.
function Graph3(Nodes, Edges) {
this.renderer = new THREE.WebGLRenderer({ alpha: true});
var width = window.innerWidth , height = window.innerHeight;
this.renderer.setSize(width, height, false);
document.body.appendChild(this.renderer.domElement);
this.scene = new THREE.Scene(),
this.camera = new THREE.PerspectiveCamera(100, width / height, 0.1, 3000),
this.controls = new THREE.OrbitControls(this.camera);
this.controls.enableKeys = true;
this.controls.enableRotate = false;
var material, geometry;
self = this;
material = new THREE.LineBasicMaterial({color: '#ccc'});
geometry = new THREE.Geometry();
geometry.vertices = Nodes.map(function(item){return new THREE.Vector3(item.pos.x,item.pos.y,item.pos.z);});
// this.vertices = geometry.vertices;
this.line = new THREE.LineSegments(geometry, material);
this.scene.add(this.line);
var Node = new THREE.Group();
material = new THREE.PointsMaterial( { color:0x000060 ,size:1 } );
this.particles = new THREE.Mesh(geometry,material)
this.particles = new THREE.Points( geometry, material);
this.scene.add( this.particles );
dragControls = new THREE.DragControls([this.particles], this.camera/*,this.scene*/, this.renderer.domElement);
this.camera.position.z = 200;
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
document.addEventListener( 'click', function ( event ) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
console.log(mouse);
}, false );
stats = new Stats();
document.body.appendChild(stats.dom);
this.animate = function()
{
raycaster.setFromCamera( mouse, self.camera );
var intersections = raycaster.intersectObject( self.particles );
intersection = ( intersections.length ) > 0 ? intersections[ 0 ] : null;
if ( intersection !== null) {
console.log(intersection);
}
requestAnimationFrame( self.animate );
stats.update();
self.renderer.render(self.scene, self.camera);
}
this.animate();}
I had able to change all the points with dragControls but can't move them seperatly
I had found EventsControls.js file which help us to handle events but I couldn't use it
Here you can check how to target individual parts of a buffer geometry with a raycaster:
https://github.com/mrdoob/three.js/blob/master/examples/webgl_interactive_buffergeometry.html
As for moving them, refer to this question and answer:
How to quickly update a large BufferGeometry?
Thanks for helping me in previous question.
I am making my points in 2d plane (z = 0) and I could making them with bufferGeometry and RawShaderMaterial but now I have another problem in dragging them, how raycaster do? it need vec3 positions but I have changed it for performance purpose.
var Geo = new THREE.BufferGeometry();
var position = new Float32Array( NodeCount * 2 );
var colors = new Float32Array( NodeCount * 3 );
var sizes = new Float32Array( NodeCount );
for ( var i = 0; i < NodeCount; i++ ) {
position[ 2*i ] = (Math.random() - 0.5) * 10;
position[ 2*i + 1 ] = (Math.random() - 0.5) * 10;
colors[ 3*i ] = Math.random();
colors[3*i+1] = Math.random();
colors[3*i+2] = Math.random();
// sizes
sizes[i] = Math.random() * 5 ;
}
Geo.addAttribute( 'position', new THREE.BufferAttribute( position, 2 ) );
Geo.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
Geo.addAttribute( 'size', new THREE.BufferAttribute( sizes, 1 ) );
points = new THREE.Points( Geo, new THREE.RawShaderMaterial({
vertexShader:`
precision highp float;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform vec3 cameraPosition;
attribute vec2 position; /// reason of problem
varying vec3 vColor;
attribute vec3 color;
attribute float size;
void main() {
vColor = color;
gl_PointSize = size;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position , 0, 1 );
}`,
fragmentShader:`
precision highp float;
varying vec3 vColor;
void main() {
gl_FragColor = vec4( vColor, 1.0 ) ;
}`
}) );
scene.add( points );
and my using of raycaster:
function mouseDown(e) {
e.preventDefault();
var mouse = new THREE.Vector2();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
// mouse.z = 0;
raycaster.setFromCamera(mouse, camera);
raycaster.far = camera.position.z + 3;
const intersect = raycaster.intersectObject(points);
console.log(intersect);
if (intersect.length > 0) {
controls.enabled = false;
console.log(intersect);
selection = intersect[0].index;
}
}
function mouseUp(e) {
controls.enabled = true;
var vector = new THREE.Vector3();
vector.x = (( event.clientX / window.innerWidth ) * 2 - 1);
vector.y = (- ( event.clientY / window.innerHeight ) * 2 + 1);
vector.z = 1.0;
console.log(camera.position.z);
vector.unproject( camera );
var dir = vector.sub( camera.position ).normalize();
var distance = - camera.position.z / dir.z;
var temp = camera.position.clone().add( dir.multiplyScalar( distance ) );
var pos = points.geometry.attributes.position;
pos.setXY(selection, temp.x, temp.y);
pos.updateRange.offset = selection; // where to start updating
pos.updateRange.count = 1; // how many vertices to update
pos.needsUpdate = true;
selection = undefined;
}
Say I have a set of four or more points that are on the perimeter of a rectangle, and that the rectangle is rotated by some unknown amount. I know that at least one point is on each side of the rectangle. One arbitrary side point is designated (0, 0), and the other points are the distance from this starting point. How can I get the non-rotated corner points of this rectangle?
assuming you're not trying to find a unique solution:
rotate your points around 0,0 until the top-most, bottom-most,
left-most, and right-most points are all different points
draw horizontal lines through the top-most and bottom-most, and vertical lines through the left-most and right-most
you're done
var points = [];
var bs = document.body.style;
var ds = document.documentElement.style;
bs.height = bs.width = ds.height = ds.width = "100%";
bs.border = bs.margin = bs.padding = 0;
var c = document.createElement("canvas");
c.style.display = "block";
c.addEventListener("mousedown", addPoint, false);
document.body.appendChild(c);
var ctx = c.getContext("2d");
var interval;
function addPoint(e) {
if (points.length >= 4) points = [];
points.push({
x: e.x - c.offsetLeft,
y: e.y - c.offsetTop
});
while (points.length > 4) points.shift();
redraw();
}
function rotateAround(a, b, r) {
d = {x:a.x - b.x, y:a.y - b.y};
return {
x: b.x + Math.cos(r) * d.x - Math.sin(r) * d.y,
y: b.y + Math.cos(r) * d.y + Math.sin(r) * d.x
}
}
function drawPoint(p) {
ctx.strokeStyle = "rgb(0,0,0)";
ctx.beginPath();
ctx.arc(p.x, p.y, 10, 0, 2 * Math.PI, true);
ctx.closePath();
ctx.stroke();
}
var last_few = [];
function redraw() {
if (interval) clearInterval(interval);
last_few = [];
c.width = window.innerWidth;
c.height = window.innerHeight;
ctx.clearRect(0, 0, c.width, c.height);
ctx.fillStyle = "rgb(200, 200, 200)";
ctx.font = "40px serif";
if (points.length < 4) {
ctx.fillText("click " + (4 - points.length) + " times", 20, 40);
points.forEach(drawPoint);
} else {
var average = {x:0, y:0};
points.forEach(function (p) {
average.x += p.x / 4;
average.y += p.y / 4;
});
var step = 0;
interval = setInterval(function () {
ctx.clearRect(0, 0, c.width, c.height);
ctx.fillText("click anywhere to start over", 20, 40);
last_few.forEach(function(r) {
ctx.strokeStyle = "rgb(200,255,200)";
ctx.save();
ctx.translate(average.x, average.y);
ctx.rotate((step -r.step) * Math.PI / 180);
ctx.strokeRect(r.lm - average.x, r.tm - average.y, (r.rm - r.lm), (r.bm - r.tm));
ctx.restore();
});
var tm = Infinity;
var bm = -Infinity;
var lm = Infinity;
var rm = -Infinity;
points.forEach(function (p) {
p = rotateAround(p, average, step * Math.PI / 180);
drawPoint(p);
tm = Math.min(p.y, tm);
bm = Math.max(p.y, bm);
lm = Math.min(p.x, lm);
rm = Math.max(p.x, rm);
});
if (points.every(function (p) {
p = rotateAround(p, average, step * Math.PI / 180);
return (p.x == lm) || (p.x == rm) || (p.y == tm) || (p.y == bm);
})) {
ctx.strokeStyle = "rgb(0,255,0)";
ctx.strokeRect(lm, tm, (rm - lm), (bm - tm));
last_few.push({tm:tm, bm:bm, lm:lm, rm:rm, step:step});
while(last_few.length > 30) last_few.shift();
} else {
ctx.strokeStyle = "rgb(255,0,0)";
ctx.strokeRect(lm, tm, (rm - lm), (bm - tm));
}
step++;
}, 30);
}
}
window.onresize = redraw;
redraw();
On d3js.org they have this sea of hexagons that is fully interactive, but there are no d3 docs that show how one would even start to make something like this.
From inspecting the source, you can see it's made with something called hexbin and d3js itself, but there's no other source code that actually helps understand how it's made.
Can anyone shed light on how they implemented this?
Thanks to Lars Kotthoff this is how they did it assuming you have a structure called data:
data.forEach(function(d, i) {
d.i = i % 10;
d.j = i / 10 | 0;
});
Math.seedrandom(+d3.time.hour(new Date));
d3.shuffle(data);
var height = 460,
imageWidth = 132,
imageHeight = 152,
radius = 75,
depth = 4;
var currentFocus = [innerWidth / 2, height / 2],
desiredFocus,
idle = true;
var style = document.body.style,
transform = ("webkitTransform" in style ? "-webkit-"
: "MozTransform" in style ? "-moz-"
: "msTransform" in style ? "-ms-"
: "OTransform" in style ? "-o-"
: "") + "transform";
var hexbin = d3.hexbin()
.radius(radius);
if (!("ontouchstart" in document)) d3.select("#examples")
.on("mousemove", mousemoved);
var deep = d3.select("#examples-deep");
var canvas = deep.append("canvas")
.attr("height", height);
var context = canvas.node().getContext("2d");
var svg = deep.append("svg")
.attr("height", height);
var mesh = svg.append("path")
.attr("class", "example-mesh");
var anchor = svg.append("g")
.attr("class", "example-anchor")
.selectAll("a");
var graphic = deep.selectAll("svg,canvas");
var image = new Image;
image.src = "ex.jpg?3f2d00ffdba6ced9c50f02ed42f12f6156368bd2";
image.onload = resized;
d3.select(window)
.on("resize", resized)
.each(resized);
function drawImage(d) {
context.save();
context.beginPath();
context.moveTo(0, -radius);
for (var i = 1; i < 6; ++i) {
var angle = i * Math.PI / 3,
x = Math.sin(angle) * radius,
y = -Math.cos(angle) * radius;
context.lineTo(x, y);
}
context.clip();
context.drawImage(image,
imageWidth * d.i, imageHeight * d.j,
imageWidth, imageHeight,
-imageWidth / 2, -imageHeight / 2,
imageWidth, imageHeight);
context.restore();
}
function resized() {
var deepWidth = innerWidth * (depth + 1) / depth,
deepHeight = height * (depth + 1) / depth,
centers = hexbin.size([deepWidth, deepHeight]).centers();
desiredFocus = [innerWidth / 2, height / 2];
moved();
graphic
.style("left", Math.round((innerWidth - deepWidth) / 2) + "px")
.style("top", Math.round((height - deepHeight) / 2) + "px")
.attr("width", deepWidth)
.attr("height", deepHeight);
centers.forEach(function(center, i) {
center.j = Math.round(center[1] / (radius * 1.5));
center.i = Math.round((center[0] - (center.j & 1) * radius * Math.sin(Math.PI / 3)) / (radius * 2 * Math.sin(Math.PI / 3)));
context.save();
context.translate(Math.round(center[0]), Math.round(center[1]));
drawImage(center.example = data[(center.i % 10) + ((center.j + (center.i / 10 & 1) * 5) % 10) * 10]);
context.restore();
});
mesh.attr("d", hexbin.mesh);
anchor = anchor.data(centers, function(d) { return d.i + "," + d.j; });
anchor.exit().remove();
anchor.enter().append("a")
.attr("xlink:href", function(d) { return d.example.url; })
.attr("xlink:title", function(d) { return d.example.title; })
.append("path")
.attr("d", hexbin.hexagon());
anchor
.attr("transform", function(d) { return "translate(" + d + ")"; });
}
function mousemoved() {
var m = d3.mouse(this);
desiredFocus = [
Math.round((m[0] - innerWidth / 2) / depth) * depth + innerWidth / 2,
Math.round((m[1] - height / 2) / depth) * depth + height / 2
];
moved();
}
function moved() {
if (idle) d3.timer(function() {
if (idle = Math.abs(desiredFocus[0] - currentFocus[0]) < .5 && Math.abs(desiredFocus[1] - currentFocus[1]) < .5) currentFocus = desiredFocus;
else currentFocus[0] += (desiredFocus[0] - currentFocus[0]) * .14, currentFocus[1] += (desiredFocus[1] - currentFocus[1]) * .14;
deep.style(transform, "translate(" + (innerWidth / 2 - currentFocus[0]) / depth + "px," + (height / 2 - currentFocus[1]) / depth + "px)");
return idle;
});
}
I'm trying to make a confetti explosion and I'm having issues with projecting the confetti out. My idea is to have a fast explosion outwards in all directions (1 sec) then the confetti floats to the ground. I'm sure my math is wrong because I'm not getting it to expand.
I've taken three.js code and made some mods:
http://givememypatientinfo.com/ParticleBlocksConfetti.html
Any suggestions are welcome. I'm a noob at the three.js... but love the library!
Code:
var container, stats;
var camera, controls, scene, projector, renderer;
var objects = [], plane;
var vel = 1;
var vel2 = 0.01;
var accel = .3;
var accel2 = -.9;
var force = 1;
var frame = 0;
var mouse = new THREE.Vector2(),
offset = new THREE.Vector3(),
INTERSECTED, SELECTED;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 1000;
/*//controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;*/
scene = new THREE.Scene();
scene.add( new THREE.AmbientLight( 0x505050 ) );
var light = new THREE.SpotLight( 0xffffff, 1.5 );
light.position.set( 0, 500, 2000 );
light.castShadow = true;
light.shadowCameraNear = 200;
light.shadowCameraFar = camera.far;
light.shadowCameraFov = 50;
light.shadowBias = -0.00022;
light.shadowDarkness = 0.5;
light.shadowMapWidth = 2048;
light.shadowMapHeight = 2048;
scene.add( light );
var geometry = new THREE.CubeGeometry( 40, 40, 40 );
//make confetti for particle system
for ( var i = 0; i < 100; i ++ ) {
var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );
//object.material.ambient = object.material.color;
/*object.position.x = Math.random() * 500 - 100;
object.position.y = Math.random() * 500 - 100;
object.position.z = 300;*/
object.position.x = Math.random() * 100 - 100;
object.position.y = Math.random() * 100 - 100;
object.position.z = 300;
object.rotation.x = Math.random() * 2 * Math.PI;
object.rotation.y = Math.random() * 2 * Math.PI;
object.rotation.z = Math.random() * 2 * Math.PI;
object.scale.x = .1;
object.scale.y = Math.random() * .8 + .1;
object.scale.z = Math.random() * .5 + .1;
object.castShadow = false;
object.receiveShadow = true;
scene.add( object );
objects.push( object );
}
plane = new THREE.Mesh( new THREE.PlaneGeometry( 2000, 2000, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0x000000, opacity: 0.25, transparent: true, wireframe: true } ) );
plane.visible = false;
scene.add( plane );
projector = new THREE.Projector();
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.sortObjects = false;
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMapEnabled = true;
renderer.shadowMapType = THREE.PCFShadowMap;
container.appendChild( renderer.domElement );
var info = document.createElement( 'div' );
info.style.position = 'absolute';
info.style.top = '10px';
info.style.width = '100%';
info.style.textAlign = 'center';
info.innerHTML = 'three.js webgl - draggable cubes';
container.appendChild( info );
}
function animate_particles(frame) {
//will update each particle
if (frame < 50){
var pCount = objects.length-1;
if (frame < 40){
vel += accel*2;
}else {
vel = vel + accel2;
}
while(pCount > -1) {
if (frame < 30){
objects[pCount].position.y += vel;
}else{
objects[pCount].position.y -= vel;
}
//objects[pCount].rotation.x += Math.random()*.7;
//objects[pCount].rotation.z += Math.random()*.01;
//objects[pCount].rotation.y += Math.random()*.01;
pCount--;
}
}
}
function animate() {
requestAnimationFrame( animate );
animate_particles(frame);
render();
//stats.update();
}
function render() {
renderer.render( scene, camera );
frame++;
}
</script>
This could be what you were trying to archieve. I modified your code a little bit and commented the changes. Basically I just added a random direction vector, normalized it and added a random speed to the particles. In the animate_particles function, I am moving the confetti along the random direction vector at the random speed.
var container, stats;
var camera, controls, scene, projector, renderer;
var objects = [], plane;
var vel = 1;
var vel2 = 0.01;
var accel = .3;
var accel2 = -.9;
var force = 1;
var frame = 0;
var mouse = new THREE.Vector2(),
offset = new THREE.Vector3(),
INTERSECTED, SELECTED;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 1000;
/*//controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;*/
scene = new THREE.Scene();
scene.add( new THREE.AmbientLight( 0x505050 ) );
var light = new THREE.SpotLight( 0xffffff, 1.5 );
light.position.set( 0, 500, 2000 );
light.castShadow = true;
light.shadowCameraNear = 200;
light.shadowCameraFar = camera.far;
light.shadowCameraFov = 50;
light.shadowBias = -0.00022;
light.shadowDarkness = 0.5;
light.shadowMapWidth = 2048;
light.shadowMapHeight = 2048;
scene.add( light );
var geometry = new THREE.CubeGeometry( 40, 40, 40 );
//make confetti for particle system
for ( var i = 0; i < 100; i ++ ) {
var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );
//object.material.ambient = object.material.color;
/*object.position.x = Math.random() * 500 - 100;
object.position.y = Math.random() * 500 - 100;
object.position.z = 300;*/
object.position.x = Math.random() * 100 - 100;
object.position.y = Math.random() * 100 - 100;
object.position.z = 300;
object.rotation.x = Math.random() * 2 * Math.PI;
object.rotation.y = Math.random() * 2 * Math.PI;
object.rotation.z = Math.random() * 2 * Math.PI;
object.scale.x = .1;
object.scale.y = Math.random() * .8 + .1;
object.scale.z = Math.random() * .5 + .1;
// give every "particle" a random expanding direction vector and normalize it to receive a length of 1.
object.directionVector = new THREE.Vector3( Math.random() - .5, Math.random() - .5, Math.random() - .5 )
object.directionVector.normalize();
// and a random expanding Speed
object.expandingSpeed = Math.random() * 100;
object.castShadow = false;
object.receiveShadow = true;
scene.add( object );
objects.push( object );
}
plane = new THREE.Mesh( new THREE.PlaneGeometry( 2000, 2000, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0x000000, opacity: 0.25, transparent: true, wireframe: true } ) );
plane.visible = false;
scene.add( plane );
projector = new THREE.Projector();
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.sortObjects = false;
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMapEnabled = true;
renderer.shadowMapType = THREE.PCFShadowMap;
container.appendChild( renderer.domElement );
var info = document.createElement( 'div' );
info.style.position = 'absolute';
info.style.top = '10px';
info.style.width = '100%';
info.style.textAlign = 'center';
info.innerHTML = 'three.js webgl - draggable cubes';
container.appendChild( info );
}
function animate_particles(frame) {
//will update each particle
if (frame < 50){
var pCount = objects.length-1;
if (frame < 40){
vel += accel*2;
}else {
vel = vel + accel2;
}
while(pCount > -1) {
if (frame < 30){
// commented that out. not sure why you put it there.
//objects[pCount].position.y += vel;
// move objects along random direction vector at the individual random speed.
objects[pCount].position.x += objects[pCount].directionVector.x * objects[pCount].expandingSpeed;
objects[pCount].position.y += objects[pCount].directionVector.y * objects[pCount].expandingSpeed;
objects[pCount].position.z += objects[pCount].directionVector.z * objects[pCount].expandingSpeed;
}else{
objects[pCount].position.y -= vel;
}
//objects[pCount].rotation.x += Math.random()*.7;
//objects[pCount].rotation.z += Math.random()*.01;
//objects[pCount].rotation.y += Math.random()*.01;
pCount--;
}
}
}
function animate() {
requestAnimationFrame( animate );
animate_particles(frame);
render();
//stats.update();
}
function render() {
renderer.render( scene, camera );
frame++;
}
I've just made a plugin for create Confetti Effect with THREE.js: https://github.com/mrgoonie/three.confetti.explosion.js
Things would be easier with:
var confetti = new ExplosionConfetti({
rate: 1, // percent of explosion in every tick - smaller is fewer - be careful, larger than 10 may crash your browser!
amount: 200, // max amount particle of an explosion
radius: 800, // max radius of an explosion
areaWidth: 500, // width of the area
areaHeight: 500, // height of the area
fallingHeight: 500, // start exploding from Y position
fallingSpeed: 1, // max falling speed
colors: [0xffffff, 0xff0000, 0xffff00] // random colors
});
scene.add( confetti.object );
Cheers,