get boundaries of complex object in image with white background (js) - javascript

What is a good way of getting the boundaries for an image (not the image itself, but rather the non-white pixels)? I am using javascript, so try to keep the algos within that realm if you can.
For example, how would I get a list/two lists of all of the x and y points where for the points that exist on the boundary of this (group of) object(s):
Note that the interior part should be included, but a lack of colour that is completely on the inside (like a hole) should be excluded.
Therefore, the result would be two lists that contain the x and y points (for the pixels) that would construct an object similar to this:
Below is how I "achieved" it. While it works for all concave objects, if you try to use it for more complex objects with some convex sides, it inevitably fails.
Success with Concave Object
jsfiddle: https://jsfiddle.net/bhc4qn87/
snippet:
var _PI = Math.PI, _HALF_PI = Math.PI / 2, _TWO_PI = 2 * Math.PI;
var _radius = 10, _damp = 75, _center = new THREE.Vector3(0, 0, 0);
var _phi = _PI / 2, _theta = _theta = _PI / 7;
var _sceneScreenshot = null, _dirty = true;
var _tmpCan = document.createElement("canvas"),
_tmpCtx = _tmpCan.getContext("2d");
var scene = document.getElementById("scene"),
sw = scene.width, sh = scene.height;
var _scene = new THREE.Scene();
var _renderer = new THREE.WebGLRenderer({ canvas: scene, alpha: true, antialias: true });
_renderer.setPixelRatio(window.devicePixelRatio);
_renderer.setSize(sw, sh);
var _camera = new THREE.PerspectiveCamera(35, sw / sh, .1, 1000);
_tmpCan.width = sw; _tmpCan.height = sh;
_scene.add(new THREE.HemisphereLight(0x999999, 0x555555, 1));
_scene.add(new THREE.AmbientLight(0x404040));
var _camLight = new THREE.PointLight(0xdfdfdf, 1.8, 300, 2);
_scene.add(_camLight);
var geometry = new THREE.BoxBufferGeometry( 1, 1, 1 );
var material = new THREE.MeshPhysicalMaterial( { color: 0x2378d3, opacity: .7 } );
var cube = new THREE.Mesh( geometry, material );
_scene.add( cube );
function initialize() {
document.body.appendChild(_tmpCan);
_tmpCan.style.position = "absolute";
_tmpCan.style.left = "8px";
_tmpCan.style.top = "8px";
_tmpCan.style.pointerEvents = "none";
addListeners();
updateCamera();
animate();
}
function addListeners() {
/* mouse events */
var scene = document.getElementById("scene");
scene.oncontextmenu = function(e) {
e.preventDefault();
}
scene.onmousedown = function(e) {
e.preventDefault();
mouseTouchDown(e.pageX, e.pageY, e.button);
}
scene.ontouchstart = function(e) {
if (e.touches.length !== 1) {
return;
}
e.preventDefault();
mouseTouchDown(e.touches[0].pageX, e.touches[0].pageY, e.touches.length, true);
}
function mouseTouchDown(pageX, pageY, button, touch) {
_mouseX = pageX; _mouseY = pageY;
_button = button;
if (touch) {
document.ontouchmove = function(e) {
if (e.touches.length !== 1) {
return;
}
mouseTouchMove(e.touches[0].pageX, e.touches[0].pageY, e.touches.length, true);
}
document.ontouchend = function() {
document.ontouchmove = null;
document.ontouchend = null;
}
} else {
document.onmousemove = function(e) {
mouseTouchMove(e.pageX, e.pageY, _button);
}
document.onmouseup = function() {
document.onmousemove = null;
document.onmouseup = null;
}
}
}
function mouseTouchMove(pageX, pageY, button, touch) {
var dx = pageX - _mouseX,
dy = pageY - _mouseY;
_phi += dx / _damp;
// _theta += dy / _damp;
_phi %= _TWO_PI;
if (_phi < 0) {
_phi += _TWO_PI;
}
// var maxTheta = _HALF_PI - _HALF_PI * .8,
// minTheta = -_HALF_PI + _HALF_PI * .8;
// if (_theta > maxTheta) {
// _theta = maxTheta;
// } else if (_theta < minTheta) {
// _theta = minTheta;
// }
updateCamera();
_dirty = true;
// updateLabels();
_mouseX = pageX;
_mouseY = pageY;
}
}
function updateCamera() {
// var radius = _radius + (Math.sin(_theta % _PI)) * 10;
var radius = _radius;
var y = radius * Math.sin(_theta),
phiR = radius * Math.cos(_theta);
var z = phiR * Math.sin(_phi),
x = phiR * Math.cos(_phi);
_camera.position.set(x, y, z);
_camLight.position.set(x, y, z);
_camera.lookAt(_center);
}
function updateLabels() {
if (_sceneScreenshot === null) {
return;
}
var tmpImg = new Image();
tmpImg.onload = function() {
_tmpCtx.drawImage(tmpImg, 0, 0, sw, sh);
var imgData = _tmpCtx.getImageData(0, 0, sw, sh);
var data = imgData.data;
var firstXs = [];
var lastXs = [];
for (var y = 0; y < sh; y++) {
var firstX = -1;
var lastX = -1;
for (var x = 0; x < sw; x++) {
var i = (x + y * sw) * 4;
var sum = data[i] + data[i + 1] + data[i + 2];
if (firstX === -1) {
if (sum > 3) {
firstX = x;
}
} else {
if (sum > 3) {
lastX = x;
}
}
}
if (lastX === -1 && firstX >= 0) {
lastX = firstX;
}
firstXs.push(firstX);
lastXs.push(lastX);
}
var firstYs = [];
var lastYs = [];
for (var x = 0; x < sw; x++) {
var firstY = -1;
var lastY = -1;
for (var y = 0; y < sh; y++) {
var i = (x + y * sw) * 4;
var sum = data[i] + data[i + 1] + data[i + 2];
if (firstY === -1) {
if (sum < 759) {
firstY = y;
}
} else {
if (sum < 759) {
lastY = y;
}
}
}
if (lastY === -1 && firstY >= 0) {
lastY = firstY;
}
firstYs.push(firstY);
lastYs.push(lastY);
}
postLoad(firstXs, lastXs, firstYs, lastYs);
}
tmpImg.src = _sceneScreenshot;
function postLoad(firstXs, lastXs, firstYs, lastYs) {
_tmpCtx.clearRect(0, 0, sw, sh);
_tmpCtx.beginPath();
for (var y = 0; y < sh; y++) {
_tmpCtx.moveTo(firstXs[y], y);
_tmpCtx.lineTo(lastXs[y], y);
}
/* TODO REMOVE BELOW TODO */
_tmpCtx.strokeStyle = 'black';
console.log(_tmpCtx.globalAlpha);
_tmpCtx.stroke();
/* TODO REMOVE ABOVE TODO */
_tmpCtx.beginPath();
for (var x = 0; x < sw; x++) {
_tmpCtx.moveTo(x, firstYs[x]);
_tmpCtx.lineTo(x, lastYs[x]);
}
/* TODO REMOVE BELOW TODO */
_tmpCtx.strokeStyle = 'black';
_tmpCtx.stroke();
/* TODO REMOVE ABOVE TODO */
var imgData = _tmpCtx.getImageData(0, 0, sw, sh);
var data = imgData.data;
for (var i = 0, iLen = data.length; i < iLen; i += 4) {
if (data[i + 3] < 200) {
data[i + 3] = 0;
}
/* TODO remove v TODO */
else { data[i + 3] = 120; }
}
_tmpCtx.putImageData(imgData, 0, 0);
}
}
function animate () {
cube.rotation.x += 0.001;
cube.rotation.y += 0.001;
_renderer.render(_scene, _camera);
if (_dirty) {
_sceneScreenshot = _renderer.domElement.toDataURL();
updateLabels();
_dirty = false;
}
requestAnimationFrame( animate );
}
initialize();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/92/three.js"></script>
<canvas id="scene" width="400" height="300"></canvas>
Failure with Complex Object
jsfiddle: https://jsfiddle.net/xdr9bt0w/
var _PI = Math.PI, _HALF_PI = Math.PI / 2, _TWO_PI = 2 * Math.PI;
var _radius = 10, _damp = 75, _center = new THREE.Vector3(0, 0, 0);
var _phi = _PI / 2, _theta = _theta = 0;
var _sceneScreenshot = null, _dirty = true;
var _tmpCan = document.createElement("canvas"),
_tmpCtx = _tmpCan.getContext("2d");
var scene = document.getElementById("scene"),
sw = scene.width, sh = scene.height;
var _scene = new THREE.Scene();
var _renderer = new THREE.WebGLRenderer({ canvas: scene, alpha: true, antialias: true });
_renderer.setPixelRatio(window.devicePixelRatio);
_renderer.setSize(sw, sh);
var _camera = new THREE.PerspectiveCamera(35, sw / sh, .1, 1000);
_tmpCan.width = sw; _tmpCan.height = sh;
_scene.add(new THREE.HemisphereLight(0x999999, 0x555555, 1));
_scene.add(new THREE.AmbientLight(0x404040));
var _camLight = new THREE.PointLight(0xdfdfdf, 1.8, 300, 2);
_scene.add(_camLight);
var geometry = new THREE.BoxBufferGeometry( 1, 1, 1 );
var material = new THREE.MeshPhysicalMaterial( { color: 0x2378d3, opacity: .7 } );
var cube = new THREE.Mesh( geometry, material );
_scene.add( cube );
var geometry = new THREE.BoxBufferGeometry( 1, 1, 1 );
var material = new THREE.MeshPhysicalMaterial( { color: 0xc36843, opacity: .7 } );
var cube2 = new THREE.Mesh( geometry, material );
cube2.position.x = -.75;
cube2.position.y = .75
_scene.add( cube2 );
var geometry = new THREE.BoxBufferGeometry( 1, 1, 1 );
var material = new THREE.MeshPhysicalMaterial( { color: 0x43f873, opacity: .7 } );
var cube3 = new THREE.Mesh( geometry, material );
cube3.position.x = -.25;
cube3.position.y = 1.5;
_scene.add( cube3 );
var geometry = new THREE.BoxBufferGeometry( 1, 1, 1 );
var material = new THREE.MeshPhysicalMaterial( { color: 0x253621, opacity: .7 } );
var cube4 = new THREE.Mesh( geometry, material );
cube4.position.x = 1;
cube4.position.y = .35;
_scene.add( cube4 );
function initialize() {
document.body.appendChild(_tmpCan);
_tmpCan.style.position = "absolute";
_tmpCan.style.left = "200px";
_tmpCan.style.top = "0px";
_tmpCan.style.pointerEvents = "none";
addListeners();
updateCamera();
animate();
}
function addListeners() {
/* mouse events */
var scene = document.getElementById("scene");
scene.oncontextmenu = function(e) {
e.preventDefault();
}
scene.onmousedown = function(e) {
e.preventDefault();
mouseTouchDown(e.pageX, e.pageY, e.button);
}
scene.ontouchstart = function(e) {
if (e.touches.length !== 1) {
return;
}
e.preventDefault();
mouseTouchDown(e.touches[0].pageX, e.touches[0].pageY, e.touches.length, true);
}
function mouseTouchDown(pageX, pageY, button, touch) {
_mouseX = pageX; _mouseY = pageY;
_button = button;
if (touch) {
document.ontouchmove = function(e) {
if (e.touches.length !== 1) {
return;
}
mouseTouchMove(e.touches[0].pageX, e.touches[0].pageY, e.touches.length, true);
}
document.ontouchend = function() {
document.ontouchmove = null;
document.ontouchend = null;
}
} else {
document.onmousemove = function(e) {
mouseTouchMove(e.pageX, e.pageY, _button);
}
document.onmouseup = function() {
document.onmousemove = null;
document.onmouseup = null;
}
}
}
function mouseTouchMove(pageX, pageY, button, touch) {
var dx = pageX - _mouseX,
dy = pageY - _mouseY;
_phi += dx / _damp;
// _theta += dy / _damp;
_phi %= _TWO_PI;
if (_phi < 0) {
_phi += _TWO_PI;
}
// var maxTheta = _HALF_PI - _HALF_PI * .8,
// minTheta = -_HALF_PI + _HALF_PI * .8;
// if (_theta > maxTheta) {
// _theta = maxTheta;
// } else if (_theta < minTheta) {
// _theta = minTheta;
// }
updateCamera();
_dirty = true;
// updateLabels();
_mouseX = pageX;
_mouseY = pageY;
}
}
function updateCamera() {
// var radius = _radius + (Math.sin(_theta % _PI)) * 10;
var radius = _radius;
var y = radius * Math.sin(_theta),
phiR = radius * Math.cos(_theta);
var z = phiR * Math.sin(_phi),
x = phiR * Math.cos(_phi);
_camera.position.set(x, y, z);
_camLight.position.set(x, y, z);
_camera.lookAt(_center);
}
function updateLabels() {
if (_sceneScreenshot === null) {
return;
}
var tmpImg = new Image();
tmpImg.onload = function() {
_tmpCtx.drawImage(tmpImg, 0, 0, sw, sh);
var imgData = _tmpCtx.getImageData(0, 0, sw, sh);
var data = imgData.data;
var firstXs = [];
var lastXs = [];
for (var y = 0; y < sh; y++) {
var firstX = -1;
var lastX = -1;
for (var x = 0; x < sw; x++) {
var i = (x + y * sw) * 4;
var sum = data[i] + data[i + 1] + data[i + 2];
if (firstX === -1) {
if (sum > 3) {
firstX = x;
}
} else {
if (sum > 3) {
lastX = x;
}
}
}
if (lastX === -1 && firstX >= 0) {
lastX = firstX;
}
firstXs.push(firstX);
lastXs.push(lastX);
}
var firstYs = [];
var lastYs = [];
for (var x = 0; x < sw; x++) {
var firstY = -1;
var lastY = -1;
for (var y = 0; y < sh; y++) {
var i = (x + y * sw) * 4;
var sum = data[i] + data[i + 1] + data[i + 2];
if (firstY === -1) {
if (sum > 3) {
firstY = y;
}
} else {
if (sum > 3) {
lastY = y;
}
}
}
if (lastY === -1 && firstY >= 0) {
lastY = firstY;
}
firstYs.push(firstY);
lastYs.push(lastY);
}
postLoad(firstXs, lastXs, firstYs, lastYs);
}
tmpImg.src = _sceneScreenshot;
function postLoad(firstXs, lastXs, firstYs, lastYs) {
_tmpCtx.clearRect(0, 0, sw, sh);
_tmpCtx.beginPath();
for (var y = 0; y < sh; y++) {
_tmpCtx.moveTo(firstXs[y], y);
_tmpCtx.lineTo(lastXs[y], y);
}
/* TODO REMOVE BELOW TODO */
_tmpCtx.strokeStyle = 'black';
console.log(_tmpCtx.globalAlpha);
_tmpCtx.stroke();
/* TODO REMOVE ABOVE TODO */
_tmpCtx.beginPath();
for (var x = 0; x < sw; x++) {
_tmpCtx.moveTo(x, firstYs[x]);
_tmpCtx.lineTo(x, lastYs[x]);
}
/* TODO REMOVE BELOW TODO */
_tmpCtx.strokeStyle = 'black';
_tmpCtx.stroke();
/* TODO REMOVE ABOVE TODO */
var imgData = _tmpCtx.getImageData(0, 0, sw, sh);
var data = imgData.data;
for (var i = 0, iLen = data.length; i < iLen; i += 4) {
if (data[i + 3] < 200) {
data[i + 3] = 0;
}
/* TODO remove v TODO */
else { data[i + 3] = 120; }
}
_tmpCtx.putImageData(imgData, 0, 0);
}
}
function animate () {
cube.rotation.x += 0.001;
cube.rotation.y += 0.001;
cube2.rotation.x -= 0.001;
cube2.rotation.y += 0.001;
cube3.rotation.x += 0.001;
cube3.rotation.y -= 0.001;
cube4.rotation.x -= 0.001;
cube4.rotation.y -= 0.001;
_renderer.render(_scene, _camera);
if (_dirty) {
_sceneScreenshot = _renderer.domElement.toDataURL();
updateLabels();
_dirty = false;
}
requestAnimationFrame( animate );
}
initialize();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/92/three.js"></script>
<canvas id="scene" width="400" height="300"></canvas>
You can see with the above jsfiddle that the inside of this complex, convex image fails on the inside.
Question
Therefore, the question remains: what is a good way of creating a mask, if you will, of the image (disregarding holes) that will cover all of the outside of any complex/convex object where the background is white and the components of the image are anything but white? thanks

Here is a solution that uses a flood-fill algorithm to cover the outer areas with white and the rest with black.
Keep in mind that this is a very naive implementation, there are lots of optimization that can potentially be done (by calculating the bounding rectangle and only filling inside it for example, another one would be to use 32-bit arrays to do the actual pixel assignment while filling).
Another thing to note is that the filling always starts in the upper left corner, if the object is currently covering that pixel it will not work (you can however pick another pixel to start at).
I removed the touch handlers and some other items to keep the example short.
The updateMask-function is where the mask is created.
function createCube(color, x, y){
const geo = new THREE.BoxBufferGeometry( 1, 1, 1 );
const mat = new THREE.MeshPhysicalMaterial( { color: color, opacity: 1 } );
const mesh = new THREE.Mesh(geo, mat);
mesh.position.x = x;
mesh.position.y = y;
return mesh;
}
const c_main = document.getElementById("main");
const c_mask = document.getElementById("mask");
const ctx_mask = c_mask.getContext("2d");
ctx_mask.fillStyle = "#000";
const cw = c_main.width, ch = c_main.height;
const TWO_PI = Math.PI * 2;
const damp = 75, radius = 10, animspeed = 0.001;
const center = new THREE.Vector3(0, 0, 0);
let x1 = 0;
let phi = Math.PI / 2;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(35, cw / ch, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ canvas: c_main, alpha: true, antialias: false });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(cw, ch);
const camLight = new THREE.PointLight(0xdfdfdf, 1.8, 300, 2);
scene.add(new THREE.HemisphereLight(0x999999, 0x555555, 1));
scene.add(new THREE.AmbientLight(0x404040));
scene.add(camLight);
const cubes = [];
cubes.push(createCube(0x2378d3, 0, 0));
cubes.push(createCube(0xc36843, -0.75, 0.75));
cubes.push(createCube(0x43f873, -0.25, 1.5));
cubes.push(createCube(0x253621, 1, 0.35));
scene.add(...cubes);
function initialize() {
c_main.addEventListener("mousedown", mouseDown, false);
updateCamera();
animate();
}
function updateMask(){
//First, fill the canvas with black
ctx_mask.globalCompositeOperation = "source-over";
ctx_mask.fillRect(0,0, cw, ch);
//Then using the composite operation "destination-in" the canvas is made transparent EXCEPT where the new image is drawn.
ctx_mask.globalCompositeOperation = "destination-in";
ctx_mask.drawImage(c_main, 0, 0);
//Now, use a flood fill algorithm of your choice to fill the outer transparent field with white.
const idata = ctx_mask.getImageData(0,0, cw, ch);
const array = idata.data;
floodFill(array, 0, 0, cw, ch);
ctx_mask.putImageData(idata, 0, 0);
//The only transparency left are in the "holes", we make these black by using the composite operation "destination-over" to paint black behind everything.
ctx_mask.globalCompositeOperation = "destination-over";
ctx_mask.fillRect(0,0, cw, ch);
}
function mouseDown(e){
e.preventDefault();
x1 = e.pageX;
const button = e.button;
document.addEventListener("mousemove", mouseMove, false);
document.addEventListener("mouseup", mouseUp, false);
}
function mouseUp(){
document.removeEventListener("mousemove", mouseMove, false);
document.removeEventListener("mouseup", mouseUp, false);
}
function mouseMove(e){
const x2 = e.pageX;
const dx = x2 - x1;
phi += dx/damp;
phi %= TWO_PI;
if( phi < 0 ){
phi += TWO_PI;
}
x1 = x2;
updateCamera();
}
function updateCamera() {
const x = radius * Math.cos(phi);
const y = 0;
const z = radius * Math.sin(phi);
camera.position.set(x, y, z);
camera.lookAt(center);
camLight.position.set(x, y, z);
}
function animate(){
cubes[0].rotation.x += animspeed;
cubes[0].rotation.y += animspeed;
cubes[1].rotation.x -= animspeed;
cubes[1].rotation.y += animspeed;
cubes[2].rotation.x += animspeed;
cubes[2].rotation.y -= animspeed;
cubes[3].rotation.x -= animspeed;
cubes[3].rotation.y -= animspeed;
renderer.render(scene, camera);
updateMask();
requestAnimationFrame(animate);
}
const FILL_THRESHOLD = 254;
//Quickly adapted flood fill from http://www.adammil.net/blog/v126_A_More_Efficient_Flood_Fill.html
function floodStart(array, x, y, width, height){
const M = width * 4;
while(true){
let ox = x, oy = y;
while(y !== 0 && array[(y-1)*M + x*4 + 3] < FILL_THRESHOLD){ y--; }
while(x !== 0 && array[y*M + (x-1)*4 + 3] < FILL_THRESHOLD){ x--; }
if(x === ox && y === oy){ break; }
}
floodFill(array, x, y, width, height);
}
function floodFill(array, x, y, width, height){
const M = width * 4;
let lastRowLength = 0;
do{
let rowLength = 0, sx = x;
let idx = y*M + x*4 + 3;
if(lastRowLength !== 0 && array[idx] >= FILL_THRESHOLD){
do{
if(--lastRowLength === 0){ return; }
}
while(array[ y*M + (++x)*4 + 3]);
sx = x;
}
else{
for(; x !== 0 && array[y*M + (x-1)*4 + 3] < FILL_THRESHOLD; rowLength++, lastRowLength++){
const idx = y*M + (--x)*4;
array[idx] = 255;
array[idx + 1] = 255;
array[idx + 2] = 255;
array[idx + 3] = 255;
if( y !== 0 && array[(y-1)*M + x*4 + 3] < FILL_THRESHOLD ){
floodStart(array, x, y-1, width, height);
}
}
}
for(; sx < width && array[y*M + sx*4 + 3] < FILL_THRESHOLD; rowLength++, sx++){
const idx = y*M + sx*4;
array[idx] = 255;
array[idx + 1] = 255;
array[idx + 2] = 255;
array[idx + 3] = 255;
}
if(rowLength < lastRowLength){
for(let end=x+lastRowLength; ++sx < end; ){
if(array[y*M + sx*4 + 3] < FILL_THRESHOLD){
floodFill(array, sx, y, width, height);
}
}
}
else if(rowLength > lastRowLength && y !== 0){
for(let ux=x+lastRowLength; ++ux<sx; ){
if(array[(y-1)*M + ux*4 + 3] < FILL_THRESHOLD){
floodStart(array, ux, y-1, width, height);
}
}
}
lastRowLength = rowLength;
}
while(lastRowLength !== 0 && ++y < height);
}
initialize();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/92/three.js"></script>
<canvas id="main" width="300" height="200"></canvas>
<canvas id="mask" width="300" height="200"></canvas>

Related

want to style the all dots of pre built html canvas background

The code is different, I apologize may I didn't ask the question the right way, you can understand well in principle, When I'm hovering the dots get colours and connect to each other, other dots are invisible, I want that all dots should be visible all time. live example available on codepen, https://codepen.io/tati420/pen/RwBamQo?editors=1111
(function () {
var width,
height,
largeHeader,
canvas,
ctx,
points,
target,
animateHeader = true;
// Main
initHeader();
initAnimation();
addListeners();
function initHeader() {
width = window.innerWidth;
height = window.innerHeight;
target = { x: width / 2, y: height / 2 };
largeHeader = document.getElementById("large-header");
largeHeader.style.height = height + "px";
canvas = document.getElementById("demo-canvas");
canvas.width = width;
canvas.height = height;
ctx = canvas.getContext("2d");
// create points
points = [];
for (var x = 0; x < width; x = x + width / 20) {
for (var y = 0; y < height; y = y + height / 20) {
var px = x + (Math.random() * width) / 20;
var py = y + (Math.random() * height) / 20;
var p = { x: px, originX: px, y: py, originY: py };
points.push(p);
}
}
// for each point find the 5 closest points
for (var i = 0; i < points.length; i++) {
var closest = [];
var p1 = points[i];
for (var j = 0; j < points.length; j++) {
var p2 = points[j];
if (!(p1 == p2)) {
var placed = false;
for (var k = 0; k < 5; k++) {
if (!placed) {
if (closest[k] == undefined) {
closest[k] = p2;
placed = true;
}
}
}
for (var k = 0; k < 5; k++) {
if (!placed) {
if (getDistance(p1, p2) < getDistance(p1, closest[k])) {
closest[k] = p2;
placed = true;
}
}
}
}
}
p1.closest = closest;
}
// assign a circle to each point
for (var i in points) {
var c = new Circle(
points[i],
2 + Math.random() * 2,
"rgba(0,255,255,0.3)"
);
points[i].circle = c;
}
}
// Event handling
function addListeners() {
if (!("ontouchstart" in window)) {
window.addEventListener("mousemove", mouseMove);
}
window.addEventListener("scroll", scrollCheck);
window.addEventListener("resize", resize);
}
function mouseMove(e) {
var posx = (posy = 0);
if (e.pageX || e.pageY) {
posx = e.pageX;
posy = e.pageY;
} else if (e.clientX || e.clientY) {
posx =
e.clientX +
document.body.scrollLeft +
document.documentElement.scrollLeft;
posy =
e.clientY +
document.body.scrollTop +
document.documentElement.scrollTop;
}
target.x = posx;
target.y = posy;
}
function scrollCheck() {
if (document.body.scrollTop > height) animateHeader = false;
else animateHeader = true;
}
function resize() {
width = window.innerWidth;
height = window.innerHeight;
largeHeader.style.height = height + "px";
canvas.width = width;
canvas.height = height;
}
// animation
function initAnimation() {
animate();
for (var i in points) {
shiftPoint(points[i]);
}
}
function animate() {
if (animateHeader) {
ctx.clearRect(0, 0, width, height);
for (var i in points) {
// detect points in range
if (Math.abs(getDistance(target, points[i])) < 4000) {
points[i].active = 0.3;
points[i].circle.active = 0.6;
} else if (Math.abs(getDistance(target, points[i])) < 20000) {
points[i].active = 0.1;
points[i].circle.active = 0.3;
} else if (Math.abs(getDistance(target, points[i])) < 40000) {
points[i].active = 0.02;
points[i].circle.active = 0.1;
} else {
points[i].active = 0;
points[i].circle.active = 0;
}
drawLines(points[i]);
points[i].circle.draw();
}
}
requestAnimationFrame(animate);
}
function shiftPoint(p) {
TweenLite.to(p, 1 + 1 * Math.random(), {
x: p.originX - 50 + Math.random() * 100,
y: p.originY - 50 + Math.random() * 100,
ease: Circ.easeInOut,
onComplete: function () {
shiftPoint(p);
},
});
}
ctx.strokeStyle = "rgba(255,0,0," + p.active + ")";
// Canvas manipulation
function drawLines(p) {
if (!p.active) return;
for (var i in p.closest) {
ctx.beginPath();
ctx.moveTo(p.x, p.y);
ctx.lineTo(p.closest[i].x, p.closest[i].y);
ctx.strokeStyle = "rgba(0,255,255," + p.active + ")";
ctx.stroke();
}
}
function Circle(pos, rad, color) {
var _this = this;
// constructor
(function () {
_this.pos = pos || null;
_this.radius = rad || null;
_this.color = color || null;
})();
this.draw = function () {
if (!_this.active) return;
ctx.beginPath();
ctx.arc(_this.pos.x, _this.pos.y, _this.radius, 0, 2 * Math.PI, false);
ctx.fillStyle = "rgba(0,255,0," + _this.active + ")";
ctx.fill();
};
}
// Util
function getDistance(p1, p2) {
return Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2);
}
})();
If I understand your question correctly, you just want to make sure that every point.active = 1 and point.circle.active = 1:
function animate() {
if (animateHeader) {
ctx.clearRect(0, 0, width, height);
for (var i in points) {
points[i].active = 1;
points[i].circle.active = 1;
drawLines(points[i]);
points[i].circle.draw();
}
}
requestAnimationFrame(animate);
}

Why won't the image load

What is wrong with the code, I can't find the problem. I think it's because of the drawFrame and loadImage function, but I don't know. So is there a way to make the image load and you will still be able to move it around and shoot?
And when I try to remove the drawFrame function nothing would work. And if posiible is there a way to change the bullet from squares to circles?
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta charset="UTF-8">
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0;
}
canvas {
cursor: crosshair;
background-color: cornflowerblue;
}
</style>
</head>
<body>
<canvas id="Trump"></canvas>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
(async function main() {
var canvas = document.getElementById('Trump');
if (!canvas.getContext) return;
var ctx = canvas.getContext('2d');
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
//const fps = 60;
const ronaNum = 30; // number of particles at start
const ronaSize = 70; // corona size
const ronaSpeed = 100; // speed
let chinabmg;
function draw() {
$("canvas").one("click", function () {
chinabmg = new sound("stankytrumpchina.mp3");
chinabmg.play();
});
//mutes when clicked on m
document.body.onkeyup = function (e) {
if (e.keyCode == 77) { chinabmg.stop() }
};
//compatability
var requestAnimationFrame = window.requestAnimationFrame || //Chromium
window.webkitRequestAnimationFrame || //Webkit
window.mozRequestAnimationFrame || //Mozilla Geko
window.oRequestAnimationFrame || //Opera Presto
window.msRequestAnimationFrame || //IE Trident?
function (callback) { //Fallback function
window.setTimeout(callback, 1000 / 60);
};
var DrawX = 0;
var DrawY = 0;
var time = 0;
var width = canvas.width;
var height = canvas.height;
var offTop = canvas.offsetTop;
var offLeft = canvas.offsetLeft;
var rectWidth = 15;
var rectHeight = 15;
var speed = 1;
var x = width / 2;
var y = height / 2;
var size = 30;
var id = 0;
var bulletId = 0;
function sound(src) {
this.sound = document.createElement("audio");
this.sound.src = src;
this.sound.setAttribute("preload", "auto");
this.sound.setAttribute("controls", "none");
this.sound.setAttribute("loop", "auto");
this.sound.style.display = "none";
document.body.appendChild(this.sound);
this.play = function () {
this.sound.play();
}
this.stop = function () {
this.sound.pause();
}
}
function player(id, color, size, x, y) {
this.id = id;
this.color = color;
this.size = size;
this.x = x;
this.y = y;
this.speed = speed;
}
var playerList = [];
function addPlayer(color, size, x, y) {
playerList[id] = new player(id, color, size, x, y);
id += 1;
}
addPlayer("blue", size, width / 2 - 50, height / 2);
var pressedKeys = [];
function moveLeft(checkId, checkX, checkY, cSize, cSpeed, cKey) {
var x = checkX - cSpeed;
var y = checkY - cSpeed;
var x2 = checkX + cSize + cSpeed;
var y2 = checkY + cSize + cSpeed;
if (x > 0) {
playerList[checkId].x = checkX - cSpeed;
} else {
playerList[checkId].x = 0;
}
}
function moveUp(checkId, checkX, checkY, cSize, cSpeed, cKey) {
var x = checkX - cSpeed;
var y = checkY - cSpeed;
var x2 = checkX + cSize + cSpeed;
var y2 = checkY + cSize + cSpeed;
if (y > 0) {
playerList[checkId].y = checkY - cSpeed;
} else {
playerList[checkId].y = 0;
}
}
function moveRight(checkId, checkX, checkY, cSize, cSpeed, cKey) {
var x = checkX - cSpeed;
var y = checkY - cSpeed;
var x2 = checkX + cSize + cSpeed;
var y2 = checkY + cSize + cSpeed;
if (x2 < width) {
playerList[checkId].x = checkX + cSpeed;
} else {
playerList[checkId].x = width - cSize;
}
}
function moveDown(checkId, checkX, checkY, cSize, cSpeed, cKey) {
var x = checkX - cSpeed;
var y = checkY - cSpeed;
var x2 = checkX + cSize + cSpeed;
var y2 = checkY + cSize + cSpeed;
if (y2 < height) {
playerList[checkId].y = checkY + cSpeed;
} else {
playerList[checkId].y = height - cSize;
}
}
function Move(checkId, checkX, checkY, cSize, cSpeed, cKey) {
if (checkId === 0) {
switch (cKey) {
case 65:
// left
moveLeft(checkId, checkX, checkY, cSize, cSpeed, cKey);
break;
case 87:
// up
moveUp(checkId, checkX, checkY, cSize, cSpeed, cKey);
break;
case 68:
// right
moveRight(checkId, checkX, checkY, cSize, cSpeed, cKey);
break;
case 83:
// down
moveDown(checkId, checkX, checkY, cSize, cSpeed, cKey);
break;
default:
return; // exit this handler for other keys
}
}
}
var validKeys = [];
validKeys[0] = "65,87,68,83-107,109,80";
// == KEYDOWN ==
$(document.body).keydown(function (e) {
e.preventDefault();
//go through all players
$.each(playerList, function (i, currentPlayer) {
if (validKeys[currentPlayer.id].indexOf(e.which) == -1) return true;
if (!pressedKeys[e.which]) {
//set interval for the function
pressedKeys[e.which] = setInterval(function () {
Move(currentPlayer.id, currentPlayer.x, currentPlayer.y, currentPlayer.size, currentPlayer.speed, e.which);
}, 5);
}
});
});
// == KEYUP ==
$(document.body).keyup(function (e) {
if (pressedKeys[e.which]) {
clearInterval(pressedKeys[e.which]);
delete pressedKeys[e.which];
}
});
//=============================== SHOOTING ===============================
//Bullets
function bullet(id, color, size, speed, x, y, eX, eY) {
this.id = id;
this.color = color;
this.size = size;
this.x = x;
this.y = y;
this.eX = eX;
this.eY = eY;
this.velocityX = 1;
this.velocityY = 1;
this.speed = 9;
}
var bulletList = [];
function addBullet(color, bsize, bspeed, x, y, eX, eY) {
bulletList[bulletId] = new bullet(bulletId, color, bsize, 9, x, y, eX, eY);
bulletId += 1;
}
function updateBullet(bullet, player) {
var dx = (bullet.eX - player.x);
var dy = (bullet.eY - player.y);
var mag = Math.sqrt(dx * dx + dy * dy);
bullet.velocityX = (dx / mag) * 9;
bullet.velocityY = (dy / mag) * 9;
bullet.x += bullet.velocityX;
bullet.y += bullet.velocityY;
}
// Add event listener for `click` events.
canvas.onmousedown = function (e) {
addBullet("#696969", 10, 2, playerList[0].x, playerList[0].y, e.x, e.y);
};
//corona part
let corona = [];
createCoronaParticles();
// game loop
//setInterval(update, 1000 / fps);
function createCoronaParticles() {
corona = [];
let cx, cy;
for (var i = 0; i < ronaNum; i++) {
do {
cx = Math.floor(Math.random() * canvas.width);
cy = Math.floor(Math.random() * canvas.height);
} while (noSpawnOnFigure(canvas.height / 2, canvas.width / 2, cy, cx) < ronaSize * 5);
corona.push(newParticle(cx, cy));
}
}
function noSpawnOnFigure(cy1, cx1, cy2, cx2) {
return Math.sqrt(Math.pow(cy2 - cy1, 2) + Math.pow(cx2 - cx1, 2));
}
function newParticle(cx, cy) {
let rona = {
ca: Math.random() * Math.PI * 2, //radians
cr: ronaSize / 2,
cx: cx,
cy: cy,
cxv: Math.random() * ronaSpeed / 60 * (Math.random() < 0.5 ? 1 : -1),
cyv: Math.random() * ronaSpeed / 60 * (Math.random() < 0.5 ? 1 : -1)
};
return rona;
}
// function update() {
// }
// ======= DRAWING =======
function drawFrame() {
requestAnimationFrame(drawFrame);
ctx.font = "15pt Georgia"; // sets the font and font size of the text
ctx.clearRect(0, 0, width, height);
$.each(playerList, function (index, currentPlayer) {
//debug
//draw players
function loadImage(path) {
let image = new Image();
let promise = new Promise((resolve, reject) => {
image.onload = () => resolve(image);
image.onerror = reject
});
image.src = path;
return promise;
}
loadImage.src = 'trump.gif';
});
//draw bullets
$.each(bulletList, function (index, bullet) {
updateBullet(bullet, playerList[0]);
ctx.fillStyle = bullet.color;
ctx.fillRect(bullet.x, bullet.y, bullet.size, bullet.size);
});
// canvas
//ctx.clearRect(0, 0, canvas.width, canvas.height);
// draw corona particles
ctx.strokeStyle = "rgb(150, 0, 0)";
ctx.lineWidth = 20;
let ca, cr, cx, cy;
for (let i = 0; i < corona.length; i++) {
// get properties
ca = corona[i].ca;
cr = corona[i].cr;
cx = corona[i].cx;
cy = corona[i].cy;
// draw path
ctx.beginPath();
ctx.fillStyle = "rgb(200, 0, 0)"
ctx.moveTo(
cx + cr * Math.cos(ca),
cy + cr * Math.sin(ca)
);
// draw circle
for (let j = 1; j < 180; j++) {
ctx.lineTo(
cx + cr * Math.cos(ca + j * Math.PI * 2 / 180),
cy + cr * Math.sin(ca + j * Math.PI * 2 / 180)
);
}
ctx.closePath();
ctx.stroke();
ctx.fill();
// move particles
corona[i].cx += corona[i].cxv;
corona[i].cy += corona[i].cyv;
// particle edge of screen
if (corona[i].cx < 0 - corona[i].cr) {
corona[i].cx = canvas.width + corona[i].cr;
} else if (corona[i].cx > canvas.width + corona[i].cr) {
corona[i].cx = 0 - corona[i].cr
}
if (corona[i].cy < 0 - corona[i].cr) {
corona[i].cy = canvas.height + corona[i].cr;
} else if (corona[i].cy > canvas.height + corona[i].cr) {
corona[i].cy = 0 - corona[i].cr
}
}
}
drawFrame();
}
$(draw);
}
)();
</script>
</body>
</html>
//draw players
function loadImage(path) {
let image = new Image();
let promise = new Promise((resolve, reject) => {
image.onload = () => resolve(image);
image.onerror = reject
});
image.src = path;
//Add the following line
ctx.drawImage(image,playerList[0].x,playerList[0].y);
return promise;
}
// change loadImage.src = 'trump.gif' into this
loadImage('trump.gif');

how to fill particles inside rectangle?

I'm looking to draw a rectangle basically text but just for clearing insight I'm working it with rectangle with small particles inside rectangle the basic I idea I got from https://yalantis.com/ but in my attempt I'm stuck here with solid filled rectangle with a color I have specified for particles. Please help me.. :)
Thanks here is my code:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Off Screen Canvas</title>
<script>
function createOffscreenCanvas() {
var offScreenCanvas = document.createElement('canvas');
offScreenCanvas.width = '1360';
offScreenCanvas.height = '400';
var context = offScreenCanvas.getContext("2d");
var W=200;
var H=200;
particleCount = 200;
particles = []; //this is an array which will hold our particles Object/Class
function Particle() {
this.x = Math.random() * W;
this.y = Math.random() * H;
this.direction ={"x": -1 + Math.random()*2, "y": -1 + Math.random()*2};
this.vx = 2 * Math.random() + 4 ;
this.vy = 2 * Math.random() + 4;
this.radius = .9 * Math.random() + 1;
this.move = function(){
this.x += this.vx * this.direction.x;
this.y += this.vy * this.direction.y;
};
this.changeDirection = function(axis){
this.direction[axis] *= -1;
};
this.draw = function() {
context.beginPath();
context.fillStyle = "#0097a7";
context.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
context.fill();
};
this.boundaryCheck = function(){
if(this.x >= W){
this.x = W;
this.changeDirection("x");
}
else if(this.x <= 0){
this.x = 0;
this.changeDirection("x");
}
if(this.y >= H){
this.y = H;
this.changeDirection("y");
}
else if(this.y <= 0){
this.y = 0;
this.changeDirection("y");
}
};
}
function createParticles(){
for (var i = particleCount-1; i >= 0; i--) {
p = new Particle();
particles.push(p);
}
}// end createParticles
function drawParticles(){
for (var i = particleCount-1; i >= 0; i--){
p = particles[i];
p.draw();
}
} //end drawParticles
function updateParticles(){
for(var i = particles.length - 1; i >=0; i--){
p = particles[i];
p.move();
p.boundaryCheck();
}
}//end updateParticle
createParticles();
var part=drawParticles();
context.fillStyle=part;
context.fillRect(W-190, H-190, W, H);
context.fill();
return offScreenCanvas;
}
function copyToOnScreen(offScreenCanvas) {
var onScreenContext=document.getElementById('onScreen').getContext('2d');
var offScreenContext = offScreenCanvas.getContext('2d');
var image=offScreenContext.getImageData(10,10,200,200);
onScreenContext.putImageData(image,offScreenCanvas.width/2,offScreenCanvas.height/4);
}
function main() {
copyToOnScreen(createOffscreenCanvas());
}
</script>
<style>
canvas {
border: 1px solid red;
}
</style>
</head>
<body onload="main()">
<canvas id="onScreen" width="1360" height="400"></canvas>
</body>
</html>
I see you have not found what you are looking for yet. Below is something quick to get you on your way. There is a whole range of stuff being used from canvas,mouse,particles, etc most of which is without comments. There is no load balancing or compliance testing and because it uses babel to be compatible with IE11 I have no clue how it runs on those browsers.
I will add to this answer some other time but for now I am a little over it.
const textList = ["1","2","3","Testing","text","to","particles"];
var textPos = 0;
function createParticles(text){
createTextMap(
text, // text to display
40, // font size
"Arial", // font
{ // style fot rendering font
fillStyle : "#6AF",
strokeStyle : "#F80",
lineWidth : 2,
lineJoin : "round",
},{ // bounding box to find a best fit for
top : 0,
left : 0,
width : canvas.width,
height : canvas.height,
}
)
}
// This function starts the animations
var started = false;
function startIt(){
started = true;
const next = ()=>{
var text = textList[(textPos++ ) % textList.length];
particles.mouseFX.dist = canvas.height / 8;
createParticles(text);
setTimeout(moveOut,text.length * 100 + 3000);
}
const moveOut = ()=>{
particles.moveOut();
setTimeout(next,2000);
}
setTimeout(next,0);
}
function setStyle(ctx,style){
Object.keys(style).forEach(key => ctx[key] = style[key]);
}
// the following function create the particles from text using a canvas
// the canvas used is dsplayed on the main canvas top left fro referance.
var tCan = createImage(100, 100); // canvas used to draw text
function createTextMap(text,size,font,style,fit){
// function to conver to colour hex value
const hex = (v)=> (v < 16 ? "0" : "") + v.toString(16);
// set up font so we can find the size.
tCan.ctx.font = size + "px " + font;
// get size of text
var width = Math.ceil(tCan.ctx.measureText(text).width + size);
// resize the canvas to fit the text
tCan.width = width;
tCan.height = Math.ceil(size *1.2);
// c is alias for context
var c = tCan.ctx;
// set up font
c.font = size + "px " + font;
c.textAlign = "center";
c.textBaseline = "middle";
// set style
setStyle(c,style);
// only do stroke and fill if they are set in styles object
if(style.strokeStyle){
c.strokeText(text, width / 2, tCan.height / 2);
}
if(style.fillStyle){
c.fillText(text, width / 2, tCan.height/ 2);
}
// prep the particles
particles.empty();
// get the pixel data
var data = c.getImageData(0,0,width,tCan.height).data;
var x,y,ind,rgb,a;
// find pixels with alpha > 128
for(y = 0; y < tCan.height; y += 1){
for(x = 0; x < width; x += 1){
ind = (y * width + x) << 2; // << 2 is equiv to * 4
if(data[ind + 3] > 128){ // is alpha above half
rgb = `#${hex(data[ind ++])}${hex(data[ind ++])}${hex(data[ind ++])}`;
// add the particle
particles.add(Vec(x, y), Vec(x, y), rgb);
}
}
}
// scale the particles to fit bounding box
var scale = Math.min(fit.width / width, fit.height / tCan.height);
particles.each(p=>{
p.home.x = ((fit.left + fit.width) / 2) + (p.home.x - (width / 2)) * scale;
p.home.y = ((fit.top + fit.height) / 2) + (p.home.y - (tCan.height / 2)) * scale;
})
.findCenter() // get center used to move particles on and off of screen
.moveOffscreen() // moves particles off the screen
.moveIn(); // set the particles to move into view.
}
// vector object a quick copy from other code.
function Vec(x,y){ // because I dont like typing in new
return new _Vec(x,y);
}
function _Vec(x = 0,y = 0){
this.x = x;
this.y = y;
return this;
}
_Vec.prototype = {
setAs(vec){
this.x = vec.x;
this.y = vec.y;
},
toString(){
return `vec : { x : ${this.x}, y : ${this.y} );`
}
}
// basic particle
const particle = {
pos : null,
delta : null,
home : null,
col : "black",
}
// array of particles
const particles = {
items : [], // actual array of particles
mouseFX : { // mouse FX
power : 20,
dist : 100,
curve : 3, // polynomial power
on : true,
},
fx : {
speed : 0.4,
drag : 0.15,
size : 4,
jiggle : 8,
},
// direction 1 move in -1 move out
direction : 1,
moveOut(){this.direction = -1; return this},
moveIn(){this.direction = 1; return this},
length : 0, // Dont touch this from outside particles.
each(callback){ // custom iteration
for(var i = 0; i < this.length; i++){
callback(this.items[i],i);
}
return this;
},
empty(){ // empty but dont dereference
this.length = 0;
return this;
},
deRef(){ // call to clear memory
this.items.length = 0;
this.length = 0;
},
add(pos,home,col){ // adds a particle
var p;
if(this.length < this.items.length){
p = this.items[this.length++];
// p.pos.setAs(pos);
p.home.setAs(home);
p.delta.x = 0;
p.delta.y = 0;
p.col = col;
}else{
this.items.push(
Object.assign(
{},
particle,
{
pos,
home,
col,
delta : Vec()
}
)
);
this.length = this.items.length
}
return this;
},
draw(){ // draws all
var p, size, sizeh;
sizeh = (size = this.fx.size) / 2;
for(var i = 0; i < this.length; i++){
p = this.items[i];
ctx.fillStyle = p.col;
ctx.fillRect(p.pos.x - sizeh, p.pos.y - sizeh, size, size);
}
},
update(){ // update all particles
var p,x,y,d;
var mP = this.mouseFX.power;
var mD = this.mouseFX.dist;
var mC = this.mouseFX.curve;
var fxJ = this.fx.jiggle;
var fxD = this.fx.drag;
var fxS = this.fx.speed;
for(var i = 0; i < this.length; i++){
p = this.items[i];
p.delta.x += (p.home.x - p.pos.x ) * fxS + (Math.random() - 0.5) * fxJ;
p.delta.y += (p.home.y - p.pos.y ) * fxS + (Math.random() - 0.5) * fxJ;
p.delta.x *= fxD;
p.delta.y *= fxD;
p.pos.x += p.delta.x * this.direction;
p.pos.y += p.delta.y * this.direction;
if(this.mouseFX.on){
x = p.pos.x - mouse.x;
y = p.pos.y - mouse.y;
d = Math.sqrt(x * x + y * y);
if(d < mD){
x /= d;
y /= d;
d /= mD;
d = (1-Math.pow(d,mC)) * mP;
p.pos.x += x * d;
p.pos.y += y * d;
}
}
}
return this;
},
findCenter(){ // find the center of particles maybe could do without
var x,y;
y = x = 0;
this.each(p => {
x += p.home.x;
y += p.home.y;
});
this.center = Vec(x / this.length, y / this.length);
return this;
},
moveOffscreen(){ // move start pos offscreen
var dist,x,y;
dist = Math.sqrt(this.center.x * this.center.x + this.center.y * this.center.y);
this.each(p => {
var d;
x = p.home.x - this.center.x;
y = p.home.y - this.center.y;
d = Math.max(0.0001,Math.sqrt(x * x + y * y)); // max to make sure no zeros
p.pos.x = p.home.x + (x / d) * dist;
p.pos.y = p.home.y + (y / d) * dist;
});
return this;
},
}
function onResize(){ // called from boilerplate
if(!started){
startIt();
}
}
/** SimpleFullCanvasMouse.js begin **/
// the following globals are available
// w, h, cw, ch, width height centerWidth centerHeight of canvas
// canvas, ctx, mouse, globalTime
//MAIN animation loop
function display() { // call once per frame
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
ctx.globalAlpha = 1; // reset alpha
ctx.clearRect(0, 0, w, h);
if(tCan){
// ctx.drawImage(tCan,0,0);
}
particles.update();
particles.draw();
}
/******************************************************************************
The code from here down is generic full page mouse and canvas boiler plate
code. As I do many examples which all require the same mouse and canvas
functionality I have created this code to keep a consistent interface. The
Code may or may not be part of the answer.
This code may or may not have ES6 only sections so will require a transpiler
such as babel.js to run on legacy browsers.
*****************************************************************************/
// V2.0 ES6 version for Stackoverflow and Groover QuickRun
var w, h, cw, ch, canvas, ctx, mouse, globalTime = 0;
// You can declare onResize (Note the capital R) as a callback that is also
// called once at start up. Warning on first call canvas may not be at full
// size.
;(function(){
const RESIZE_DEBOUNCE_TIME = 100;
var resizeTimeoutHandle;
var firstRun = true;
function createCanvas () {
var c,cs;
cs = (c = document.createElement("canvas")).style;
cs.position = "absolute";
cs.top = cs.left = "0px";
cs.zIndex = 1000;
document.body.appendChild(c);
return c;
}
function resizeCanvas () {
if (canvas === undefined) { canvas = createCanvas() }
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx = canvas.getContext("2d");
if (typeof setGlobals === "function") { setGlobals() }
if (typeof onResize === "function") {
clearTimeout(resizeTimeoutHandle);
if (firstRun) { onResize() }
else { resizeTimeoutHandle = setTimeout(onResize, RESIZE_DEBOUNCE_TIME) }
firstRun = false;
}
}
function setGlobals () {
cw = (w = canvas.width) / 2;
ch = (h = canvas.height) / 2;
}
mouse = (function () {
function preventDefault(e) { e.preventDefault() }
var m; // alias for mouse
var mouse = {
x : 0, y : 0, w : 0, // mouse position and wheel
alt : false, shift : false, ctrl : false, // mouse modifiers
buttonRaw : 0,
over : false, // true if mouse over the element
buttonOnMasks : [0b1, 0b10, 0b100], // mouse button on masks
buttonOffMasks : [0b110, 0b101, 0b011], // mouse button off masks
active : false,
bounds : null,
eventNames : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(","),
event(e) {
var t = e.type;
m.bounds = m.element.getBoundingClientRect();
m.x = e.pageX - m.bounds.left - scrollX;
m.y = e.pageY - m.bounds.top - scrollY;
m.alt = e.altKey;
m.shift = e.shiftKey;
m.ctrl = e.ctrlKey;
if (t === "mousedown") { m.buttonRaw |= m.buttonOnMasks[e.which - 1] }
else if (t === "mouseup") { m.buttonRaw &= m.buttonOffMasks[e.which - 1] }
else if (t === "mouseout") { m.over = false }
else if (t === "mouseover") { m.over = true }
else if (t === "mousewheel") {m.w = e.wheelDelta }
else if (t === "DOMMouseScroll") { m.w = -e.detail }
if (m.callbacks) { m.callbacks.forEach(c => c(e)) }
if ((m.buttonRaw & 2) && m.crashRecover !== null) {
if (typeof m.crashRecover === "function") { setTimeout(m.crashRecover, 0) }
}
e.preventDefault();
},
addCallback(callback) {
if (typeof callback === "function") {
if (m.callbacks === undefined) { m.callbacks = [callback] }
else { m.callbacks.push(callback) }
}
},
start(element) {
if (m.element !== undefined) { m.remove() }
m.element = element === undefined ? document : element;
m.eventNames.forEach(name => document.addEventListener(name, mouse.event) );
document.addEventListener("contextmenu", preventDefault, false);
m.active = true;
},
remove() {
if (m.element !== undefined) {
m.eventNames.forEach(name => document.removeEventListener(name, mouse.event) );
document.removeEventListener("contextmenu", preventDefault);
m.element = m.callbacks = undefined;
m.active = false;
}
}
}
m = mouse;
return mouse;
})();
function done() { // Clean up. Used where the IDE is on the same page.
window.removeEventListener("resize", resizeCanvas)
mouse.remove();
document.body.removeChild(canvas);
canvas = ctx = mouse = undefined;
}
function update(timer) { // Main update loop
if(ctx === undefined){ return }
globalTime = timer;
display(); // call demo code
requestAnimationFrame(update);
}
setTimeout(function(){
canvas = createCanvas();
mouse.start(canvas, true);
resizeCanvas();
if(typeof Groover !== "undefined" && Groover.ide){ mouse.crashRecover = done }; // Requires Ace.js and GrooverDev.js. Prevents
window.addEventListener("resize", resizeCanvas);
requestAnimationFrame(update);
},0);
})();
/** SimpleFullCanvasMouse.js end **/
/** CreateImage.js begin **/
// creates a blank image with 2d context
function createImage(w,h){var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i;}
/** CreateImage.js end **/
canvas {
background : black;
}
well after all the attempts like hell I finally got my answer thanks to moƔois answer to my post using particle slider. here is the code. Thanks everyone for your help :)

(Three.js) Change color on particles forming image

I'm currently working on a particle system where the particles move around and create pictures with some seconds in between (almost like a slide). I know where to put the color code to change the color on the particles forming the pictures bur for some reason it doesn't work. I therefore suspect that the problem is somewhere else in the script but the question is where...
You can see the code below (tried to make a codepen but it didn't work):
var dispersed = false;
var firstDone = false;
var secondDone = false;
var thirdDone = false;
var fRgba = []; // first img rgba data
var sRgba = []; // second img rgba data
var tRgba = []; // third img rgba data
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight;
var VIEW_ANGLE = 45,
ASPECT = WIDTH / HEIGHT,
NEAR = 0.01,
FAR = 10000;
var $container = $("#container");
var renderer = new THREE.WebGLRenderer();
var camera = new THREE.PerspectiveCamera(
VIEW_ANGLE,
ASPECT,
NEAR,
FAR);
var scene = new THREE.Scene();
scene.add(camera);
camera.position.z = 900;
renderer.setSize(WIDTH, HEIGHT);
$container.append(renderer.domElement);
var particleCount = 5200,
particles = new THREE.Geometry();
var pMaterial = new THREE.PointsMaterial({
size: 6,
map: createCircleTexture('#CACACA', 256),
transparent: true,
depthWrite: false
});
function createCircleTexture(color, size) {
var matCanvas = document.createElement('canvas');
matCanvas.width = matCanvas.height = size;
var matContext = matCanvas.getContext('2d');
var texture = new THREE.Texture(matCanvas);
var center = size / 2;
matContext.beginPath();
matContext.arc(center, center, size/2, 0, 2 * Math.PI, false);
matContext.closePath();
matContext.fillStyle = color;
matContext.fill();
texture.needsUpdate = true;
return texture;
}
for (var i = 0; i < particleCount; i++) {
var x = Math.random() * 1600 - 800;
var y = getRandomInt(600, 1500)
var z = Math.random() * 30 - 15;
var particle = new THREE.Vector3(x, y, z);
particle.updated = 0;
particles.vertices.push(particle);
};
var particleSystem = new THREE.Points(particles, pMaterial);
particleSystem.sortParticles = true;
scene.add(particleSystem);
function drawImage(imageObj, array) {
var canvas = $("#canvas")[0];
var context = canvas.getContext("2d");
var imageX = 0;
var imageY = 0;
var imageWidth = imageObj.width;
var imageHeight = imageObj.height;
context.drawImage(imageObj, imageX, imageY);
var imageData = context.getImageData(imageX, imageY, imageWidth,
imageHeight);
var data = imageData.data;
for(var y = 0; y < imageHeight; y+= 4) {
for(var x = 0; x < imageWidth; x+= 4) {
var red = data[((imageWidth * y) + x) * 4];
var green = data[((imageWidth * y) + x) * 4 + 1];
var blue = data[((imageWidth * y) + x) * 4 + 2];
var alpha = data[((imageWidth * y) + x) * 4 + 3];
if (red < 100) {
var pX = (x % 500) - 249;
var pY = 249 - y;
array.push([pX, pY, red, green, blue, alpha]);
}
}
}
};
var addDestination = function(particle, x, y, z) {
var dest = new THREE.Vector3(x, y, z);
particle.destination = dest;
};
var addVelocity = function(particle) {
var xDiff = (particle.destination.x - particle.x) / 180;
var yDiff = (particle.destination.y - particle.y) / 180;
var zDiff = (particle.destination.z - particle.z) / 180;
var vel = new THREE.Vector3(xDiff, yDiff, zDiff);
particle.velocity = vel;
};
var move = function(particle) {
particle.x += particle.velocity.x;
particle.y += particle.velocity.y;
particle.z += particle.velocity.z;
particle.updated += 1;
};
var slowDown = function(particle) {
particle.velocity.x -= (particle.velocity.x / 300)
particle.velocity.y -= (particle.velocity.y / 300)
particle.velocity.z -= (particle.velocity.z / 160)
};
var resetProperties = function() {
var pCount = particleCount;
while (pCount--) {
var particle = particles.vertices[pCount];
particle.destination = null
particle.updated = 0;
};
};
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
};
var distributedZ = function(level) {
var z;
if (level === 1) {
z = getRandomInt(50, 100);
} else if (level === 2) {
z = getRandomInt(350, 400);
} else {
z = getRandomInt(650, 700);
}
return z;
};
function shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
while (0 !== currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
};
var disperse = function() {
pCount = particleCount;
for (var i = 0; i < pCount; i++) {
var particle = particles.vertices[i];
if (typeof(particle.destination) === "undefined") {
var nums = [-1, 1];
var x = particle.x + nums[Math.round(Math.random())];
var y = particle.y - 1000;
var z = Math.random() * 30 - 15;
addDestination(particle, x, y, z);
particle.velocity = new THREE.Vector3(x - particle.x, -3, z -
particle.z);
}
if (particle.updated <= 300) {
move(particle);
} else {
particles.vertices = shuffle(particles.vertices);
resetProperties();
dispersed = true;
return;
}
}
}
var morphImageParticles = function(imageParticles, rgba) {
for (var i = 0; i < imageParticles.length; i++) {
var particle = imageParticles[i]
if (particle.destination === null) {
var pixelData = rgba[i];
var x = pixelData[0];
var y = pixelData[1];
var z = Math.random() * 15 - 7;
addDestination(particle, x, y, z);
addVelocity(particle);
}
if (particle.updated <= 180) {
move(particle);
}
}
};
var morphOuterParticles = function(outerParticles, ord) {
for (var i = 0; i < outerParticles.length; i++) {
var nums = [-1, 1];
var particle = outerParticles[i];
if (particle.destination === null) {
var x = Math.random() * 1000 - 500;
var y = Math.random() * 1000 - 500;
var z;
if (i <= Math.round(outerParticles.length * 0.6)) {
z = distributedZ(1)
} else if (i > Math.round(outerParticles.length * 0.6) && i <
Math.round(outerParticles.length * 0.9)) {
z = distributedZ(2)
} else {
z = distributedZ(3);
}
addDestination(particle, x, y, z);
addVelocity(particle);
}
if (particle.updated <= 600) {
move(particle);
slowDown(particle);
} else {
particles.vertices = shuffle(particles.vertices);
resetProperties();
if (ord === 1) {
firstDone = true;
} else if (ord === 2) {
secondDone = true;
} else {
thirdDone = true;
}
return;
}
}
};
var makeImg = function(rgba, ord) {
var pCount = particleCount;
var imagePs = particles.vertices.slice(0, rgba.length);
var outerPs = particles.vertices.slice(rgba.length, pCount);
morphImageParticles(imagePs, rgba);
morphOuterParticles(outerPs, ord);
};
var update = function() {
if (thirdDone) {
} else if (secondDone) {
makeImg(tRgba, 3);
} else if (firstDone) {
makeImg(sRgba, 2);
} else if (dispersed) {
makeImg(fRgba, 1);
} else {
disperse();
}
particleSystem.geometry.verticesNeedUpdate = true;
renderer.render(scene, camera);
requestAnimationFrame(update);
TWEEN.update();
};
var rotXScale = d3.scale.linear().domain([0, window.innerHeight]).range([15,
-15]);
var rotYScale = d3.scale.linear().domain([0, window.innerWidth]).range([25,
-25]);
d3.select("body").on("mousemove", function() {
var scaledX = rotXScale(d3.mouse(this)[1]) * Math.PI / 180;
var scaledY = rotYScale(d3.mouse(this)[0]) * Math.PI / 180;
var tween = new TWEEN.Tween(particleSystem.rotation).to({ x: scaledX, y:
scaledY, z: 0 });
tween.easing( TWEEN.Easing.Quartic.Out);
tween.start();
transparency: true
});
var img1 = new Image();
var img2 = new Image();
var img3 = new Image();
img1.onload = function() {
drawImage(this, fRgba);
img2.onload = function() {
drawImage(this, sRgba);
img3.onload = function() {
drawImage(this, tRgba);
}
img3.src = "images/p1.png";
}
img2.src = "images/p2.png";
update();
}
img1.src = "images/p3.png";
update();
I thought I only need to add the code below, for example ['0xffffff'], that's how it should work at least but it didn't. Therefore I guess the problem is somewhere else in the script.
var fRgba = []; // first img rgba data
var sRgba = []; // second img rgba data
var tRgba = []; // third img rgba data

Draw random coloured circles on canvas

I am trying to create a animation where circles run from right to left. The circles' colours are selected randomly by a function. I have created a fiddle where one circle runs from right to left. Now my function creates a random colour. This function is executed every second and the circle changes its colour every second, instead of a new circle with the random picked colour become created. How can I change it so that it draws a new circle every second on the canvas and doesn't only change the colour of the circle?
This is my function:
function getRandomElement(array) {
if (array.length == 0) {
return undefined;
}
return array[Math.floor(Math.random() * array.length)];
}
var circles = [
'#FFFF00',
'#FF0000',
'#0000FF'
];
function drawcircles() {
ctx.beginPath();
ctx.arc(ballx * 108, canvasHeight / 2, x*5, 0, 2*Math.PI, false);
ctx.fillStyle = getRandomElement(circles);
ctx.fill();
ctx.closePath;
}
Comments on your question:
You are changing the color of the fillStyle to a random color at each frame. This is the reason why it keeps "changing" color. Set it to the color of the circle:
context.fillStyle = circle.color;
make circles with x, y, diameter, bounciness, speed, and color using an array
draw and update them with requestAnimationFrame (mine is a custom function)
My answer:
I made this just last night, where some circles follow the cursor and "bounce" off the edges of the screen. I tested the code and it works.
I might post a link later, but here is all of the code right now...
<!DOCTYPE html>
<html>
<body>
<canvas id="canvas"></canvas>
<script>
var lastTime = 0;
function requestMyAnimationFrame(callback, time)
{
var t = time || 16;
var currTime = new Date().getTime();
var timeToCall = Math.max(0, t - (currTime - lastTime));
var id = window.setTimeout(function(){ callback(currTime + timeToCall); }, timeToCall);
lastTime = currTime + timeToCall;
return id;
}
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
canvas.width = window.innerWidth - 20;
canvas.height = window.innerHeight - 20;
canvas.style.width = canvas.width + "px";
canvas.style.height = canvas.height + "px";
var circles = [];
var mouse =
{
x: 0,
y: 0
}
function getCoordinates(x, y)
{
return "(" + x + ", " + y + ")";
}
function getRatio(n, d)
{
// prevent division by 0
if (d === 0 || n === 0)
{
return 0;
}
else
{
return n/d;
}
}
function Circle(x,y,d,b,s,c)
{
this.x = x;
this.y = y;
this.diameter = Math.round(d);
this.radius = Math.round(d/2);
this.bounciness = b;
this.speed = s;
this.color = c;
this.deltaX = 0;
this.deltaY = 0;
this.drawnPosition = "";
this.fill = function()
{
context.beginPath();
context.arc(this.x+this.radius,this.y+this.radius,this.radius,0,Math.PI*2,false);
context.closePath();
context.fill();
}
this.clear = function()
{
context.fillStyle = "#ffffff";
this.fill();
}
this.draw = function()
{
if (this.drawnPosition !== getCoordinates(this.x, this.y))
{
context.fillStyle = this.color;
// if commented, the circle will be drawn if it is in the same position
//this.drawnPosition = getCoordinates(this.x, this.y);
this.fill();
}
}
this.keepInBounds = function()
{
if (this.x < 0)
{
this.x = 0;
this.deltaX *= -1 * this.bounciness;
}
else if (this.x + this.diameter > canvas.width)
{
this.x = canvas.width - this.diameter;
this.deltaX *= -1 * this.bounciness;
}
if (this.y < 0)
{
this.y = 0;
this.deltaY *= -1 * this.bounciness;
}
else if (this.y+this.diameter > canvas.height)
{
this.y = canvas.height - this.diameter;
this.deltaY *= -1 * this.bounciness;
}
}
this.followMouse = function()
{
// deltaX/deltaY will currently cause the circles to "orbit" around the cursor forever unless it hits a wall
var centerX = Math.round(this.x + this.radius);
var centerY = Math.round(this.y + this.radius);
if (centerX < mouse.x)
{
// circle is to the left of the mouse, so move the circle to the right
this.deltaX += this.speed;
}
else if (centerX > mouse.x)
{
// circle is to the right of the mouse, so move the circle to the left
this.deltaX -= this.speed;
}
else
{
//this.deltaX = 0;
}
if (centerY < mouse.y)
{
// circle is above the mouse, so move the circle downwards
this.deltaY += this.speed;
}
else if (centerY > mouse.y)
{
// circle is under the mouse, so move the circle upwards
this.deltaY -= this.speed;
}
else
{
//this.deltaY = 0;
}
this.x += this.deltaX;
this.y += this.deltaY;
this.x = Math.round(this.x);
this.y = Math.round(this.y);
}
}
function getRandomDecimal(min, max)
{
return Math.random() * (max-min) + min;
}
function getRoundedNum(min, max)
{
return Math.round(getRandomDecimal(min, max));
}
function getRandomColor()
{
// array of three colors
var colors = [];
// go through loop and add three integers between 0 and 255 (min and max color values)
for (var i = 0; i < 3; i++)
{
colors[i] = getRoundedNum(0, 255);
}
// return rgb value (RED, GREEN, BLUE)
return "rgb(" + colors[0] + "," + colors[1] + ", " + colors[2] + ")";
}
function createCircle(i)
{
// diameter of circle
var minDiameter = 25;
var maxDiameter = 50;
// bounciness of circle (changes speed if it hits a wall)
var minBounciness = 0.2;
var maxBounciness = 0.65;
// speed of circle (how fast it moves)
var minSpeed = 0.3;
var maxSpeed = 0.45;
// getRoundedNum returns a random integer and getRandomDecimal returns a random decimal
var x = getRoundedNum(0, canvas.width);
var y = getRoundedNum(0, canvas.height);
var d = getRoundedNum(minDiameter, maxDiameter);
var c = getRandomColor();
var b = getRandomDecimal(minBounciness, maxBounciness);
var s = getRandomDecimal(minSpeed, maxSpeed);
// create the circle with x, y, diameter, bounciness, speed, and color
circles[i] = new Circle(x,y,d,b,s,c);
}
function makeCircles()
{
var maxCircles = getRoundedNum(2, 5);
for (var i = 0; i < maxCircles; i++)
{
createCircle(i);
}
}
function drawCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].draw();
ii++;
}
}
}
function clearCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].clear();
ii++;
}
}
}
function updateCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].keepInBounds();
circles[i].followMouse();
ii++;
}
}
}
function update()
{
requestMyAnimationFrame(update,10);
updateCircles();
}
function draw()
{
requestMyAnimationFrame(draw,1000/60);
context.clearRect(0,0,canvas.width,canvas.height);
drawCircles();
}
function handleError(e)
{
//e.preventDefault();
//console.error(" ERROR ------ " + e.message + " ------ ERROR ");
}
window.addEventListener("load", function()
{
window.addEventListener("error", function(e)
{
handleError(e);
});
window.addEventListener("resize", function()
{
canvas.width = window.innerWidth - 20;
canvas.height = window.innerHeight - 20;
});
window.addEventListener("mousemove", function(e)
{
mouse.x = e.layerX || e.offsetX;
mouse.y = e.layerY || e.offsetY;
});
makeCircles();
update();
draw();
});
</script>
</body>
</html>

Categories

Resources