Draggable sprites on a wallpaper - javascript

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/

Related

Chart.js intersection not working without zero value on Y

Here's code that I took from another topic:
var ORDER_STATS = {
"2016": [0, 400010, 400110, 400110, 401000, 401000, 400100, 401000, 400001],
"Source": [330865, 332865, 318865, 332865, 320865, 334865, 322865, 320865, 340865],
"Moving average LONG": [304493, 315040, 325809, 329532, 332643, 330421, 329754, 327309, 326865]
};
var colors = ['206,191,26', '119,206,26', '26,200,206', '236,124,98', '206,26,140', '26,77,206', '236,124,98', '206,26,140', '26,77,206'];
// Definning X
var ordersChartData = {
labels: ['2022-02-10', '2022-02-11', '2022-02-12', '2022-02-13', '2022-02-14', '2022-02-15', '2022-02-16', '2022-02-17', '2022-02-18'],
datasets: []
}
Object.keys(ORDER_STATS).forEach(function(key) {
color = colors.shift();
ordersChartData.datasets.push({
label: key,
lineTension: 0,
type: 'line',
backgroundColor: "rgba(" + color + ",0.1)",
borderColor: "rgba(" + color + ",1)",
borderWidth: 2,
pointBackgroundColor: "rgba(" + color + ",1)",
pointBorderColor: "#fff",
pointBorderWidth: 1,
pointRadius: 4,
pointHoverBackgroundColor: "#fff",
pointHoverBorderColor: "rgba(" + color + ",1)",
pointHoverBorderWidth: 1,
data: ORDER_STATS[key]
});
});
var ctx = document.getElementById("myChart").getContext("2d");
Chart.defaults.global.defaultFontColor = 'grey';
Chart.defaults.global.defaultFontFamily = "Tahoma";
Chart.defaults.global.defaultFontSize = 11;
Chart.defaults.global.defaultFontStyle = 'normal';
var myChart = new Chart(ctx, {
type: 'line',
data: ordersChartData,
defaultFontSize: 11,
options: {
responsive: true,
title: {
display: true,
text: 'Intersection realization',
fontColor: "#444",
fontFamily: 'Tahoma',
padding: 0
},
legend: {
display: true,
labels: {
fontColor: 'grey',
usePointStyle: true
}
},
tooltips: {
mode: "index",
intersect: true,
position: 'nearest',
bodySpacing: 4
}
}
});
Chart.plugins.register({
afterDatasetsDraw: function(chartInstance, easing) {
var Y = chartInstance.scales['y-axis-0'];
var X = chartInstance.scales['x-axis-0'];
zeroPointY = Y.top + ((Y.bottom - Y.top) / (Y.ticks.length - 1) * Y.zeroLineIndex);
zeroPointX = Y.right;
yScale = (Y.bottom - Y.top) / (Y.end - Y.start);
xScale = (X.right - X.left) / (X.ticks.length - 1);
console.log("aaa1", Y.top, Y.bottom, Y.ticks.length, Y.zeroLineIndex, zeroPointY);
console.log("aaa2", Y.bottom, Y.top, Y.end, Y.start, yScale);
var intersects = findIntersects(ORDER_STATS['Source'], ORDER_STATS['Moving average LONG']);
var context = chartInstance.chart.ctx;
intersects.forEach(function(result, idx) {
context.fillStyle = 'red';
context.beginPath();
context.arc((result.x * xScale) + zeroPointX, (Y.end - Y.start) - (result.y * yScale) - ((Y.end - Y.start) - zeroPointY), 3, 0, 2 * Math.PI, true);
context.fill();
});
}
});
function findIntersects(line1, line2) {
var intersects = [];
line1.forEach(function(val, idx) {
var line1StartX = idx;
var line1StartY = line1[idx];
var line1EndX = idx + 1;
var line1EndY = line1[idx + 1];
var line2StartX = idx;
var line2StartY = line2[idx];
var line2EndX = idx + 1;
var line2EndY = line2[idx + 1];
result = checkLineIntersection(line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY);
if (result.onLine1 && result.onLine2) {
intersects.push(result);
}
});
return intersects;
}
function checkLineIntersection(line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY) {
// if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite) and booleans for whether line segment 1 or line segment 2 contain the point
var denominator, a, b, numerator1, numerator2, result = {
x: null,
y: null,
onLine1: false,
onLine2: false
};
denominator = ((line2EndY - line2StartY) * (line1EndX - line1StartX)) - ((line2EndX - line2StartX) * (line1EndY - line1StartY));
if (denominator == 0) {
return result;
}
a = line1StartY - line2StartY;
b = line1StartX - line2StartX;
numerator1 = ((line2EndX - line2StartX) * a) - ((line2EndY - line2StartY) * b);
numerator2 = ((line1EndX - line1StartX) * a) - ((line1EndY - line1StartY) * b);
a = numerator1 / denominator;
b = numerator2 / denominator;
// if we cast these lines infinitely in both directions, they intersect here:
result.x = line1StartX + (a * (line1EndX - line1StartX));
result.y = line1StartY + (a * (line1EndY - line1StartY));
// it is worth noting that this should be the same as:
x = line2StartX + (b * (line2EndX - line2StartX));
y = line2StartX + (b * (line2EndY - line2StartY));
// if line1 is a segment and line2 is infinite, they intersect if:
if (a > 0 && a < 1) {
result.onLine1 = true;
}
// if line2 is a segment and line1 is infinite, they intersect if:
if (b > 0 && b < 1) {
result.onLine2 = true;
}
// if line1 and line2 are segments, they intersect if both of the above are true
return result;
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.js"></script>
<canvas id="myChart" width="650" height="241" style="display: block; width: 650px; height: 241px;"></canvas>
It's working great, but if here:
"2016" : [0, 400010, 400110, 400110, 401000, 401000, 400100, 401000, 400001]
I change first value, 0, on 300000, code can't show intersection anymore.
Problem is, as I think, in Y.zeroLineIndex.
I've tried a lot of variants, logged to console almost all values and noticed that when first value is zero (like in example), Y.zeroLineIndex is 9. But if u change first value to 300000, it becomes to -1.
I am unsure how to fix it after many hours attempting to detect a problem and fix it. Nothing helped.
In JS I'm not good, so requesting for a help
EDIT:
[DEMO][1]
[1]: https://i.stack.imgur.com/0t3Gu.png
I sat down a bit on your problem and found out that you had a problem setting the height according to the Y-axis. Use this code and you will see that it works in any situation. I made changes on zeroPointY, yScale and on Y value of context.arc function.
Chart.plugins.register({
afterDatasetsDraw: function (chartInstance, easing) {
var Y = chartInstance.scales['y-axis-0'];
var X = chartInstance.scales['x-axis-0'];
zeroPointY = (Y.bottom - Y.top)/(Y.ticks.length-1);
zeroPointX = Y.right;
yScale = (Y.end - Y.start)/ (Y.ticks.length - 1);
xScale = (X.right - X.left) / (X.ticks.length - 1);
console.log("aaa1", Y.top, Y.bottom, Y.ticks.length, Y.zeroLineIndex, zeroPointY);
console.log("aaa2", Y.bottom, Y.top, Y.end, Y.start, yScale);
var intersects = findIntersects(ORDER_STATS['Source'], ORDER_STATS['Moving average LONG']);
var context = chartInstance.chart.ctx;
intersects.forEach(function (result1, idx) {
context.fillStyle = 'red';
context.beginPath();
context.arc((result1.x * xScale) + zeroPointX, Y.top + (Y.end - result1.y)/yScale*zeroPointY, 3, 0, Math.PI * 2, true);
context.fill();
});
}
});

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>

How to change the color of this (pure js)?

I am attempting to implement this mousefollow into my website:
http://codepen.io/hakimel/pen/KanIi
But I would like my own custom colors instead of what is default displayed. I changed the fillcolor value and was able to change to one specific color but I would like 4 different colors. So I have tried adding specific colors to this function next to fillColor: but no such luck. I also created a fillcolor2: property like:
for (var i = 0; i < QUANTITY; i++) {
var particle = {
size: 1,
position: { x: mouseX, y: mouseY },
offset: { x: 0, y: 0 },
shift: { x: mouseX, y: mouseY },
speed: 0.01+Math.random()*0.04,
targetSize: 1,
fillColor: '#bf3e27',
fillColor2: '#1c305c',
orbit: RADIUS*.1 + (RADIUS * .5 * Math.random())
};
and added:
context.fillStyle = particle.fillColor2;
context.strokeStyle = particle.fillColor2;
But that did not work either. I have also tried to copy and paste the same js code just to see if it would work, and just changed the fillcolor, but it would only display the last one pasted.
Can anyone show me how to get 4 separate colors the easiest way, I feel like I am vastly over-complicating this but obviously a beginner and getting rather frustrated with this?
Lastly, I would like the 4 different colors to span different radii and I messed around with the different RADIUS variables but it is pretty much impossible to figure out how to accomplish what I would like while only having one color. So there will be 4 of each color, I changed the QUANTITY to:
var QUANTITY = 16;
I need the first 4 colors radius of 10 so for the first one I set:
var RADIUS = 10;
Ideally I need the first 4 to be color (#AAAAAA) radius of 10 like it is, but need the second 4 to be color (#BBBBBBB) between radius 10 and 30, the third color (#CCCCCC) to be between radius of 30-50, and the last fourth color (#DDDDDD) to be between 50 and 70.
Any suggestions?
You could replace the definition of QUANTITY, COLOR and RADIUS with an array of these, and at the same time define ranges for RADIUS:
var GROUPS = [
{
QUANTITY: 4,
RADIUS: [ 5, 10],
COLOR: 0x888888
},
{
QUANTITY: 4,
RADIUS: [10, 30],
COLOR: 0xAA80AA
},
{
QUANTITY: 4,
RADIUS: [30, 50],
COLOR: 0xA0A0CC
},
{
QUANTITY: 4,
RADIUS: [50, 70],
COLOR: 0xFFE0E0
}
];
Then inside the createParticles function you would iterate over those GROUPS:
for (var g = 0; g < GROUPS.length; g++) {
var attribs = GROUPS[g];
for (var i = 0; i < attribs.QUANTITY; i++) {
var particle = {
size: 1,
position: { x: mouseX, y: mouseY },
offset: { x: 0, y: 0 },
shift: { x: mouseX, y: mouseY },
speed: 0.01+Math.random()*0.04,
targetSize: 1,
fillColor: '#' + attribs.COLOR.toString(16),
orbit: attribs.RADIUS[0] +
(attribs.RADIUS[1]-attribs.RADIUS[0]) * Math.random()
};
particles.push( particle );
}
}
Here is a snippet:
// One of my first <canvas> experiments, woop! :D
var SCREEN_WIDTH = window.innerWidth;
var SCREEN_HEIGHT = window.innerHeight;
var GROUPS = [
{
QUANTITY: 4,
RADIUS: [ 5, 10],
COLOR: 0x888888
},
{
QUANTITY: 4,
RADIUS: [10, 30],
COLOR: 0xAA80AA
},
{
QUANTITY: 4,
RADIUS: [30, 50],
COLOR: 0xA0A0CC
},
{
QUANTITY: 4,
RADIUS: [50, 70],
COLOR: 0xFFE0E0
}
];
var RADIUS_SCALE = 1;
var RADIUS_SCALE_MIN = 1;
var RADIUS_SCALE_MAX = 1.5;
var canvas;
var context;
var particles;
var mouseX = SCREEN_WIDTH * 0.5;
var mouseY = SCREEN_HEIGHT * 0.5;
var mouseIsDown = false;
function init() {
canvas = document.getElementById( 'world' );
if (canvas && canvas.getContext) {
context = canvas.getContext('2d');
// Register event listeners
window.addEventListener('mousemove', documentMouseMoveHandler, false);
window.addEventListener('mousedown', documentMouseDownHandler, false);
window.addEventListener('mouseup', documentMouseUpHandler, false);
document.addEventListener('touchstart', documentTouchStartHandler, false);
document.addEventListener('touchmove', documentTouchMoveHandler, false);
window.addEventListener('resize', windowResizeHandler, false);
createParticles();
windowResizeHandler();
setInterval( loop, 1000 / 60 );
}
}
function createParticles() {
particles = [];
for (var g = 0; g < GROUPS.length; g++) {
var attribs = GROUPS[g];
for (var i = 0; i < attribs.QUANTITY; i++) {
var particle = {
size: 1,
position: { x: mouseX, y: mouseY },
offset: { x: 0, y: 0 },
shift: { x: mouseX, y: mouseY },
speed: 0.01+Math.random()*0.04,
targetSize: 1,
fillColor: '#' + attribs.COLOR.toString(16),
orbit: attribs.RADIUS[0] +
(attribs.RADIUS[1]-attribs.RADIUS[0]) * Math.random()
};
particles.push( particle );
}
}
}
function documentMouseMoveHandler(event) {
mouseX = event.clientX - (window.innerWidth - SCREEN_WIDTH) * .5;
mouseY = event.clientY - (window.innerHeight - SCREEN_HEIGHT) * .5;
}
function documentMouseDownHandler(event) {
mouseIsDown = true;
}
function documentMouseUpHandler(event) {
mouseIsDown = false;
}
function documentTouchStartHandler(event) {
if(event.touches.length == 1) {
event.preventDefault();
mouseX = event.touches[0].pageX - (window.innerWidth - SCREEN_WIDTH) * .5;;
mouseY = event.touches[0].pageY - (window.innerHeight - SCREEN_HEIGHT) * .5;
}
}
function documentTouchMoveHandler(event) {
if(event.touches.length == 1) {
event.preventDefault();
mouseX = event.touches[0].pageX - (window.innerWidth - SCREEN_WIDTH) * .5;;
mouseY = event.touches[0].pageY - (window.innerHeight - SCREEN_HEIGHT) * .5;
}
}
function windowResizeHandler() {
SCREEN_WIDTH = window.innerWidth;
SCREEN_HEIGHT = window.innerHeight;
canvas.width = SCREEN_WIDTH;
canvas.height = SCREEN_HEIGHT;
}
function loop() {
if( mouseIsDown ) {
RADIUS_SCALE += ( RADIUS_SCALE_MAX - RADIUS_SCALE ) * (0.02);
}
else {
RADIUS_SCALE -= ( RADIUS_SCALE - RADIUS_SCALE_MIN ) * (0.02);
}
RADIUS_SCALE = Math.min( RADIUS_SCALE, RADIUS_SCALE_MAX );
context.fillStyle = 'rgba(0,0,0,0.05)';
context.fillRect(0, 0, context.canvas.width, context.canvas.height);
for (i = 0, len = particles.length; i < len; i++) {
var particle = particles[i];
var lp = { x: particle.position.x, y: particle.position.y };
// Rotation
particle.offset.x += particle.speed;
particle.offset.y += particle.speed;
// Follow mouse with some lag
particle.shift.x += ( mouseX - particle.shift.x) * (particle.speed);
particle.shift.y += ( mouseY - particle.shift.y) * (particle.speed);
// Apply position
particle.position.x = particle.shift.x + Math.cos(i + particle.offset.x) * (particle.orbit*RADIUS_SCALE);
particle.position.y = particle.shift.y + Math.sin(i + particle.offset.y) * (particle.orbit*RADIUS_SCALE);
// Limit to screen bounds
particle.position.x = Math.max( Math.min( particle.position.x, SCREEN_WIDTH ), 0 );
particle.position.y = Math.max( Math.min( particle.position.y, SCREEN_HEIGHT ), 0 );
particle.size += ( particle.targetSize - particle.size ) * 0.05;
if( Math.round( particle.size ) == Math.round( particle.targetSize ) ) {
particle.targetSize = 1 + Math.random() * 7;
}
context.beginPath();
context.fillStyle = particle.fillColor;
context.strokeStyle = particle.fillColor;
context.lineWidth = particle.size;
context.moveTo(lp.x, lp.y);
context.lineTo(particle.position.x, particle.position.y);
context.stroke();
context.arc(particle.position.x, particle.position.y, particle.size/2, 0, Math.PI*2, true);
context.fill();
}
}
window.onload = init;
body {
background-color: #000000;
padding: 0;
margin: 0;
overflow: hidden;
}
<canvas id='world'></canvas>
Define the settings for the particles in an array
// I've changed the color for better visibility
var ParticleSettings = [
{color: "#f00", radiusMin: 10, radiusMax: 10},
{color: "#0f0", radiusMin: 10, radiusMax: 30},
{color: "#00f", radiusMin: 30, radiusMax: 50}
];
To get the correct settings for a particle (in this case its index) use this function
function getParticleSettings(particleIndex) {
var settingsIndex = Math.floor(particleIndex / 4) % ParticleSettings.length,
settings = ParticleSettings[settingsIndex];
return {
color: settings.color,
radius: getRandom(settings.radiusMin, settings.radiusMax)
};
}
getRandom is just a small helper function to get a random number between two boundaries
function getRandom(min, max) {
return Math.floor((Math.random() * (max - min)) + min)
}
In createParticle we then have to change the for loop to get the settings for the current particle
for (var i = 0; i < QUANTITY; i++) {
// get the settings for the current particle
var particleSettings = getParticleSettings(i);
var particle = {
size: 1,
position: { x: mouseX, y: mouseY },
offset: { x: 0, y: 0 },
shift: { x: mouseX, y: mouseY },
speed: 0.01+Math.random()*0.04,
targetSize: 1,
fillColor: particleSettings.color, // <--
orbit: particleSettings.radius // <--
};
particles.push( particle );
}
This would be the result of the changes above.
You could define the colors in an array since you are using a for loop you can index them like this:
var colors = ['#AAAAAA', '#BBBBBB', '#CCCCCC', '#DDDDDD'];
var particle = {
...
fillColor: colors[i],
...
}
This will now only work for the first four colors, but since you stated you have a quantity of 16, you could use 2 for loops to achieve this. Something like this:
//will run 16 times
for(var i = 0; i < QUANTITY; i++) {
for(var x = 0; x < 4; x++) {
var particle = {
//note that i'm using x here since the array has only 4 keys.
fillColor: colors[x];
}
}
}

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

Kinetic.js – creating a grid

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);​

Categories

Resources