I try to create this sphere in three.js. I need clickable dotes and meshes. So I think, that I can do this if I will give name for every dot in sphere.
Two questions:
1. How I can randomize positions of dots on sphere?
2. How I should to give names for dots and meshes?
THREE.IcosahedronGeometry = function(radius, detail) {
var t = (1 + Math.sqrt(5)) / 2;
var vertices = [-1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, 0,
0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t,
t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, 0,
0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t,
t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1
];
var indices = [
2,10,6,0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11,
1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8,
3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9,
4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1
];
THREE.PolyhedronGeometry.call(this, vertices, indices, radius, detail);
this.type = 'IcosahedronGeometry';
this.parameters = {
radius: radius,
detail: detail
};
};
THREE.IcosahedronGeometry.prototype = Object.create(THREE.PolyhedronGeometry.prototype);
THREE.IcosahedronGeometry.prototype.constructor = THREE.IcosahedronGeometry;
// Scene
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer({
antialias: 1
});
renderer.setClearColor(0xf7f7f7);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
scene.fog = new THREE.Fog(0xd4d4d4, 8, 20);
// Create vertex points
var mesh = new THREE.IcosahedronGeometry(10, 2); // radius, detail
var vertices = mesh.vertices;
var positions = new Float32Array(vertices.length * 3);
for (var i = 0, l = vertices.length; i < l; i++) {
vertices[i].toArray(positions, i * 3);
}
alert(positions);
var geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
var material = new THREE.PointsMaterial({
size: 0.4,
vertexColors: THREE.VertexColors,
color: 0x252525
});
var points = new THREE.Points(geometry, material);
var object = new THREE.Object3D();
object.add(points);
object.add(new THREE.Mesh(
mesh,
new THREE.MeshPhongMaterial({
color: 0x616161,
emissive: 0xa1a1a1,
wireframe: true,
fog: 1
})
));
scene.add(object);
camera.position.z = 20;
var render = function() {
requestAnimationFrame(render);
object.rotation.x += 0.001;
object.rotation.y += 0.001;
renderer.render(scene, camera);
};
render();
http://codepen.io/anon/pen/ZLpPxB
here is an example how you could randomize the points: http://codepen.io/usefulthink/pen/WRoboo?editors=0010
The interesting bit is this:
var v3 = new THREE.Vector3();
var spherical = new THREE.Spherical();
for (var i = 0; i < vertices.length; i += 3) {
v3.fromArray(vertices, i);
spherical.setFromVector3(v3);
spherical.phi += rnd(variation, -variation);
spherical.theta += rnd(variation, -variation);
v3.setFromSpherical(spherical);
v3.toArray(vertices, i);
}
In short, a good way to manipulate points on the surface of a sphere is to use spherical-coordinates (phi/theta/r instead of x/y/z, think latitude/longitude). In this case I convert each of the vertices into the spherical form, manipulate the theta/phi-values a bit (keeping the radius as it is) and write the modified values back to the vertices-array.
I am not sure what you mean with giving names to these points. Something like this?
var pointNames = {
frank: 12,
maria: 7,
steve: 3,
melissa: 4
};
function getPointByName(name) {
const startIndex = pointNames[name] * 3;
return new THREE.Vector3().fromArray(vertices, index);
}
Related
I decided against using sprites and to create the tile dynamically with PIXI.Graphics, but I'm not sure where to start. I can create basic shapes like squares and rectangles but I need some help in a tile.
The closest I can get is this
var graphics = new PIXI.Graphics();
graphics.beginFill(0x989865);
graphics.lineStyle(1, 0x8E8E5E);
graphics.endFill();
graphics.drawRect(50, 50, 50, 50);
app.stage.addChild(graphics);
Something like the image below is what I'm looking for
This is my first attempt, made by trial and error (it seems to work as intended).
PIXI.utils.skipHello();
var app = new PIXI.Application({
width: 800,
height: 600,
backgroundColor: 0x000000,
antialias: true,
autoDensity: true,
resolution: 2
});
document.body.appendChild(app.view);
var stage = app.stage;
renderIsometricTile(stage, 20, 20, 50, 5, 0x989865, 0x767643, 0x545421);
renderIsometricTile(stage, 60, 40, 50, 5, 0x2A63FF, 0x0841DD, 0x0620AA);
renderIsometricTile(stage, 148, 39, 50, 5, 0x59CD90, 0x37AB70, 0x257750);
renderIsometricTile(stage, 200, 200, 150, 10, 0x3D3D8B, 0x1B1B6A, 0x1B1B48);
renderIsometricTile(stage, 100, 150, 75, 20, 0xFFD23F, 0xDDB01D, 0xBB7A0A);
function renderIsometricTile(stage, x, y, size, height, topColor, leftColor, rightColor) {
var topSide = new PIXI.Graphics();
topSide.beginFill(topColor);
topSide.drawRect(0, 0, size, size);
topSide.endFill();
topSide.setTransform(x, y + size * 0.5, 1, 1, 0, 1.1, -0.5, 0, 0);
var leftSide = new PIXI.Graphics();
leftSide.beginFill(leftColor);
leftSide.drawRect(0, 0, height, size);
leftSide.endFill();
leftSide.setTransform(x, y + size * 0.5, 1, 1, 0, 1.1, 1.57, 0, 0);
var rightSide = new PIXI.Graphics();
rightSide.beginFill(rightColor);
rightSide.drawRect(0, 0, size, height);
rightSide.endFill();
rightSide.setTransform(x, y + size * 0.5, 1, 1, 0, -0.0, -0.5, -(size + (size * 0.015)), -(size - (size * 0.06)));
stage.addChild(topSide);
stage.addChild(leftSide);
stage.addChild(rightSide);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.2.1/pixi.min.js"></script>
Trying to paint each cube face with a different color, I found a thread that presents a way to achieve this:
var geometry = new THREE.BoxGeometry(5, 5, 5);
for (var i = 0; i < geometry.faces.length; i++) {
geometry.faces[i].color.setHex(Math.random() * 0xffffff);
}
var material = new THREE.MeshBasicMaterial({
color: 0xffffff,
vertexColors: THREE.FaceColors
});
But with three.js r86, I get the following result:
Got the triangles that make up each face, painted individually.
To achieve the desirable effect, I used the following adaptation of the above code:
var geometry = new THREE.BoxGeometry(5, 5, 5);
for ( var i = 0; i < geometry.faces.length; i += 2 ) {
var faceColor = Math.random() * 0xffffff;
geometry.faces[i].color.setHex(faceColor);
geometry.faces[i+1].color.setHex(faceColor);
}
var material = new THREE.MeshBasicMaterial({
color: 0xffffff,
vertexColors: THREE.FaceColors
});
But this all seems a bit over worked!
'use strict';
var camera, scene, renderer, cube;
init();
render();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
// renderer
renderer = new THREE.WebGLRenderer({
alpha: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
camera.position.z = 12;
// Mesh - cube
var geometry = new THREE.BoxGeometry(5, 5, 5);
for (var i = 0; i < geometry.faces.length; i += 2) {
var faceColor = Math.random() * 0xffffff;
geometry.faces[i].color.setHex(faceColor);
geometry.faces[i + 1].color.setHex(faceColor);
}
var material = new THREE.MeshBasicMaterial({
color: 0xffffff,
vertexColors: THREE.FaceColors
});
cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// Light
var pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.x = 10;
pointLight.position.y = 50;
pointLight.position.z = 130;
scene.add(pointLight);
}
function render() {
cube.rotation.x = 16;
cube.rotation.y = 4;
cube.rotation.z -= 5;
renderer.render(scene, camera);
}
body,
canvas {
margin: 0;
padding: 0;
}
body {
overflow: hidden;
background-color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.js"></script>
Am I missing something on three.js to accomplish the face painting as a whole ?
If you switch to BufferGeometry you can use groups to control the material of sections of your geometry. Groups are based on the vertex indices, and allow you to define a material index, which will reference a material inside an array of materials.
Consider:
// start, count, material index
bufferGeometry.addGroup(12, 6, 2)
This tells the geometry to start a new group of triangles at indices index 12, and accounts for 6 indices (which reference 6 vertices). The final parameter tells the group of triangles to use material index 2 (index 2 of the array of materials you use to create the mesh).
In the example below, I've given each side of a cube a different color. You might think this is the same effect as setting face colors, but note that this is setting a material per group, not just a color, which can lead to creating some really cool effects.
var renderer, scene, camera, controls, stats, mesh;
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000;
function populateScene() {
var bg = new THREE.BufferGeometry();
bg.addAttribute("position", new THREE.BufferAttribute(new Float32Array([
// front
-1, 1, 1, // 0
-1, -1, 1, // 1
1, 1, 1, // 2
1, -1, 1, // 3
// right
1, 1, 1, // 4
1, -1, 1, // 5
1, 1, -1, // 6
1, -1, -1, // 7
// back
1, 1, -1, // 8
1, -1, -1, // 9
-1, 1, -1, // 10
-1, -1, -1, // 11
// left
-1, 1, -1, // 12
-1, -1, -1, // 13
-1, 1, 1, // 14
-1, -1, 1, // 15
// top
-1, 1, -1, // 16
-1, 1, 1, // 17
1, 1, -1, // 18
1, 1, 1, // 19
// bottom
-1, -1, 1, // 20
-1, -1, -1, // 21
1, -1, 1, // 22
1, -1, -1 // 23
]), 3));
bg.addAttribute("normal", new THREE.BufferAttribute(new Float32Array([
// front
0, 0, 1, // 0
0, 0, 1, // 1
0, 0, 1, // 2
0, 0, 1, // 3
// right
1, 0, 0, // 4
1, 0, 0, // 5
1, 0, 0, // 6
1, 0, 0, // 7
// back
0, 0, -1, // 8
0, 0, -1, // 9
0, 0, -1, // 10
0, 0, -1, // 11
// left
-1, 0, 0, // 12
-1, 0, 0, // 13
-1, 0, 0, // 14
-1, 0, 0, // 15
// top
0, 1, 0, // 16
0, 1, 0, // 17
0, 1, 0, // 18
0, 1, 0, // 19
// bottom
0, -1, 0, // 20
0, -1, 0, // 21
0, -1, 0, // 22
0, -1, 0 // 23
]), 3));
bg.setIndex(new THREE.BufferAttribute(new Uint32Array([
// front 0
0, 1, 2,
3, 2, 1,
// right 6
4, 5, 6,
7, 6, 5,
// back 12
8, 9, 10,
11, 10, 9,
// left 18
12, 13, 14,
15, 14, 13,
// top 24
16, 17, 18,
19, 18, 17,
// bottom 30
20, 21, 22,
23, 22, 21
]), 1));
bg.clearGroups();
// start, count, material index
bg.addGroup(0, 6, 0);
bg.addGroup(6, 6, 1);
bg.addGroup(12, 6, 2);
bg.addGroup(18, 6, 3);
bg.addGroup(24, 6, 4);
bg.addGroup(30, 6, 5);
var materials = [
new THREE.MeshLambertMaterial({color:"red"}),
new THREE.MeshLambertMaterial({color:"green"}),
new THREE.MeshLambertMaterial({color:"blue"}),
new THREE.MeshLambertMaterial({color:"cyan"}),
new THREE.MeshLambertMaterial({color:"magenta"}),
new THREE.MeshLambertMaterial({color:"yellow"})
];
mesh = new THREE.Mesh(bg, materials);
mesh.scale.set(5, 5, 5);
scene.add(mesh);
}
function init() {
document.body.style.backgroundColor = "slateGray";
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
document.body.style.overflow = "hidden";
document.body.style.margin = "0";
document.body.style.padding = "0";
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 50;
scene.add(camera);
controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.dynamicDampingFactor = 0.5;
controls.rotateSpeed = 3;
var light = new THREE.PointLight(0xffffff, 1, Infinity);
camera.add(light);
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0';
document.body.appendChild(stats.domElement);
resize();
window.onresize = resize;
populateScene();
animate();
}
function resize() {
WIDTH = window.innerWidth;
HEIGHT = window.innerHeight;
if (renderer && camera && controls) {
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
controls.handleResize();
}
}
function render() {
renderer.render(scene, camera);
}
function animate() {
mesh.rotation.x += 0.015;
mesh.rotation.y += 0.017;
mesh.rotation.z += 0.019;
requestAnimationFrame(animate);
render();
controls.update();
stats.update();
}
function threeReady() {
init();
}
(function() {
function addScript(url, callback) {
callback = callback || function() {};
var script = document.createElement("script");
script.addEventListener("load", callback);
script.setAttribute("src", url);
document.head.appendChild(script);
}
addScript("https://threejs.org/build/three.js", function() {
addScript("https://threejs.org/examples/js/controls/TrackballControls.js", function() {
addScript("https://threejs.org/examples/js/libs/stats.min.js", function() {
threeReady();
})
})
})
})();
Edit: Adding a second example using the base BoxBufferGeometry
Based on pailhead's comment to the original post, here's a snippet which uses unmodified BoxBufferGeometry. But as they mentioned in their comment, you'll still need to know which group corresponds to which face.
var renderer, scene, camera, controls, stats, mesh;
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000;
function populateScene() {
var bg = new THREE.BoxBufferGeometry(1, 1, 1);
var materials = [
new THREE.MeshLambertMaterial({color:"red"}),
new THREE.MeshLambertMaterial({color:"green"}),
new THREE.MeshLambertMaterial({color:"blue"}),
new THREE.MeshLambertMaterial({color:"cyan"}),
new THREE.MeshLambertMaterial({color:"magenta"}),
new THREE.MeshLambertMaterial({color:"yellow"})
];
mesh = new THREE.Mesh(bg, materials);
mesh.scale.set(10, 10, 10);
scene.add(mesh);
}
function init() {
document.body.style.backgroundColor = "slateGray";
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
document.body.style.overflow = "hidden";
document.body.style.margin = "0";
document.body.style.padding = "0";
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 50;
scene.add(camera);
controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.dynamicDampingFactor = 0.5;
controls.rotateSpeed = 3;
var light = new THREE.PointLight(0xffffff, 1, Infinity);
camera.add(light);
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0';
document.body.appendChild(stats.domElement);
resize();
window.onresize = resize;
populateScene();
animate();
}
function resize() {
WIDTH = window.innerWidth;
HEIGHT = window.innerHeight;
if (renderer && camera && controls) {
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
controls.handleResize();
}
}
function render() {
renderer.render(scene, camera);
}
function animate() {
mesh.rotation.x += 0.015;
mesh.rotation.y += 0.017;
mesh.rotation.z += 0.019;
requestAnimationFrame(animate);
render();
controls.update();
stats.update();
}
function threeReady() {
init();
}
(function() {
function addScript(url, callback) {
callback = callback || function() {};
var script = document.createElement("script");
script.addEventListener("load", callback);
script.setAttribute("src", url);
document.head.appendChild(script);
}
addScript("https://threejs.org/build/three.js", function() {
addScript("https://threejs.org/examples/js/controls/TrackballControls.js", function() {
addScript("https://threejs.org/examples/js/libs/stats.min.js", function() {
threeReady();
})
})
})
})();
Using groups will split the geometry in 6 faces, for drawing a simple cube you can also use a simple custom ShaderMaterial.
Splitting geometry in 6 groups requires more draw calls, instead of using 1 draw call for drawing a cube you are using 6, one for each face.
Using a ShaderMaterial requires only 1 draw call:
Vertex Shader:
attribute vec3 vertexColor;
varying vec3 vColor;
void main() {
vColor = vertexColor;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.);
}
Fragment Shader:
varying vec3 vColor;
void main() {
gl_FragColor = vec4(vColor, 1.);
}
This way you could also use GLSL color blending for merging different colors.
Custom ShaderMaterial just setting vertex and fragment shader source strings:
const ColorCubeShader = function () {
THREE.ShaderMaterial.call(this, {
vertexShader: vertexShaderSrc,
fragmentShader: fragmentShaderSrc
})
}
ColorCubeShader.prototype = Object.create(THREE.ShaderMaterial.prototype)
ColorCubeShader.prototype.constructor = ColorCubeShader
Color Cube custom Mesh:
/**
* Convenience method for coloring a face
* #param {Number} r
* #param {Number} g
* #param {Number} b
* #returns {Array}
*/
const buildVertexColorArrayFace = function (r, g, b) {
return [
r, g, b,
r, g, b,
r, g, b,
r, g, b
]
}
const ColorCube = function (size) {
const geometry = new THREE.BoxBufferGeometry(size, size, size)
// build color array
let colorArray = []
colorArray = colorArray
.concat(buildVertexColorArrayFace(1, 0, 0))
.concat(buildVertexColorArrayFace(0, 1, 0))
.concat(buildVertexColorArrayFace(0, 0, 1))
.concat(buildVertexColorArrayFace(1, 0, 1))
.concat(buildVertexColorArrayFace(1, 1, 0))
.concat(buildVertexColorArrayFace(0, 1, 1))
// create a buffer attribute for the colors (for attribute vec3 vertexColor)
const colorAttribute = new THREE.Float32BufferAttribute(
new Float32Array(colorArray), 3)
// set attribute vertexColor in vertex shader
geometry.setAttribute('vertexColor', colorAttribute)
// custom Shader Material instance
const material = new ColorCubeShader()
THREE.Mesh.call(this, geometry, material)
}
ColorCube.prototype = Object.create(THREE.Mesh.prototype)
ColorCube.prototype.constructor = ColorCube
Use it:
const cube = new ColorCube(1)
cube.position.set(0, 2, -2)
scene.add(cube)
I have been trying to draw a very basic rectangle using THREE.BufferGeometry(). Here is the code -
var geometry = new THREE.BufferGeometry();
var material = new THREE.MeshBasicMaterial({color: 'rgb(255, 0, 0)'});
var verticesArray = [20, 0, 0, 0, 20, 0, -20, 0, 0, 0, -20, 0];
var vertices = new Float32Array(verticesArray, 0, 12);
var indicesArray = [0, 1, 2, 0, 2, 3];
var indices = new Uint16Array(indicesArray, 0, 6);
geometry.addAttribute('position', new THREE.BufferAttribute(vertices, 3));
//geometry.addAttribute('index', new THREE.BufferAttribute(indices, 3));
geometry.setIndex(new THREE.BufferAttribute(indices, 3));
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
Unfortunately it isn't working. It isn't throwing any exception either. To my understanding, i have done everything right. I have four vertices, and i have described two set of indices in counter clockwise order.
Could anyone please tell me what can go wrong here?
It should be geometry.setIndex(new THREE.BufferAttribute(indices, 1));
I have been using a particle system to draw on a <canvas>, and now I'm trying to create an "image" out of particles from an array.
Unfortunately, I cannot seem to figure out how to position and color the particles according to how the array is structured.
The array I'm trying to make the image out of is the "defs" array. Hopefully I'm close to achieving my goal, but I need some help.
Here's what I've got.
0 = White
1 = Black
2 = Red
(Stackoverflow wouldn't let me enter in the Javascript for some reason, here is a link to the fiddle - http://jsfiddle.net/arcbpred/)
// Creating the Canvas
var canvas = document.createElement("canvas"),
c = canvas.getContext("2d"),
particles = {},
particleIndex = 0,
particleNum = 87;
document.body.appendChild(canvas);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.id = "canvas";
// Setting needed varialbes
var xmin = 1;
var xmax = +canvas.width;
var bgcolor = "#FFF";
// Setting color which is just one big square
c.fillStyle = bgcolor;
c.fillRect(0, 0, canvas.width, canvas.height);
var defs = {
[0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0], [0, 1, 2, 2, 2, 1, 0, 1, 2, 2, 2, 1, 0], [1, 2, 3, 3, 2, 2, 1, 2, 2, 2, 2, 2, 1], [1, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1], [0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0], [0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0], [0, 0, 0, 1, 2, 2, 2, 2, 2, 1, 0, 0, 0], [0, 0, 0, 0, 1, 2, 2, 2, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
};
function Particle() {
this.x = canvas.width / 2 - 15;
this.y = canvas.height / 2 / 2;
this.vx = 0;
this.vy = 0;
this.gravity = 0;
particleIndex++;
particles[particleIndex] = this;
this.id = particleIndex;
this.beat = 1;
this.size = 30;
this.color = "rgba(191,30,46,0)";
}
Particle.prototype.draw = function() {
this.x += this.vx;
this.y += this.vy;
this.vy += this.gravity;
c.fillStyle = this.color;
c.fillRect(this.x, this.y, this.size, this.size);
};
setInterval(function() {
c.fillStyle = bgcolor;
c.fillRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < particleNum; i++) {
new Particle();
}
for (var i in particles) {
particles[i].draw();
}
}, 30);
body{
margin: 0;
padding: 0;
background-color: rgba(100,0,0,1);
overflow:hidden;
}
Thank you for any input.
I am writing a basic chess gui in html5/javascript and I have a question on how to avoid the flicker when I redraw the canvas control. Basically I am drawing the chess pieces from a 2D array and every time I redraw the array, I clear the canvas which creates a slight flicker. What would be the best way to avoid this? Thank you in advance, Dave.
//Array of chess pieces
var PieceArray = ["Null", "WhiteKing", "WhiteQueen", "WhiteKnight", "WhiteBishop", "WhiteRook", "WhitePawn", "BlackKing", "BlackQueen", "BlackKnight", "BlackBishop", "BlackRook", "BlackPawn"]
//Current state of the chess pieces on the board
var BoardArray = [[11, 9, 10, 8, 7, 10, 9, 11],
[12, 12, 12, 12, 12, 12, 12, 12],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[6, 6, 6, 6, 6, 6, 6, 6],
[5, 3, 4, 2, 1, 4, 3, 5]];
//Param1: Image Url
//Param2: X position
//Param3: Y position
function Draw(image, x, y) {
var can = document.getElementById('ChessBoard');
var context = can.getContext('2d');
context.clearRect(0, 0, can.width, can.height);
var imageObj = new Image();
imageObj.src = 'Sprites/' + image + ".png";
imageObj.onload = function () {
context.drawImage(imageObj, x, y);
};
}
//Function that draws the chess pieces to the canvas
function DrawPieces() {
var array2;
for (var i = 0; i < BoardArray.length; i++) {
array2 = BoardArray[i];
for (var x = 0; x < array2.length; x++) {
if (array2[x] != "Null") {
Draw(PieceArray[array2[x]], x * 70, i * 70);
}
}
}
}
You are doing unnecessary re-loading and DOM look-ups. The reloading of the image will be the cause in this case as the image may not be able to decode and get ready before you draw it.
Cache those things outside your draw method and it should work:
var can = document.getElementById('ChessBoard');
var context = can.getContext('2d');
var imageObj = new Image();
imageObj.onload = function () {
/// start you loop/logic here instead...
DrawPieces()
};
imageObj.src = 'Sprites/' + image + ".png";
function Draw(image, x, y) {
context.drawImage(imageObj, x, y);
}
//Function that draws the chess pieces to the canvas
function DrawPieces() {
/// also move clear here or none of the pieces but
/// the last will show
context.clearRect(0, 0, can.width, can.height);
var array2;
for (var i = 0; i < BoardArray.length; i++) {
array2 = BoardArray[i];
for (var x = 0; x < array2.length; x++) {
if (array2[x] != "Null") {
Draw(PieceArray[array2[x]], x * 70, i * 70);
}
}
}
}
Note that when the image has loaded then you go to next step (loop or input logic etc.).
Maybe something like this will work? (not tested)
//Array of chess pieces
var PieceArray = ["Null", "WhiteKing", "WhiteQueen", "WhiteKnight", "WhiteBishop", "WhiteRook", "WhitePawn", "BlackKing", "BlackQueen", "BlackKnight", "BlackBishop", "BlackRook", "BlackPawn"]
var PieceImages = {};
//Current state of the chess pieces on the board
var BoardArray = [[11, 9, 10, 8, 7, 10, 9, 11],
[12, 12, 12, 12, 12, 12, 12, 12],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[6, 6, 6, 6, 6, 6, 6, 6],
[5, 3, 4, 2, 1, 4, 3, 5]];
//Param1: Image Url
//Param2: X position
//Param3: Y position
function Draw(image, x, y) {
var can = document.getElementById('ChessBoard');
var context = can.getContext('2d');
context.clearRect(0, 0, can.width, can.height);
if (PieceImages[image]) {
if (PieceImages[image].complete) {
context.drawImage(PieceImages[image], x, y);
}
}
else {
PieceImages[image] = new Image();
PieceImages[image].src = 'Sprites/' + image + ".png";
}
}
//Function that draws the chess pieces to the canvas
function DrawPieces() {
var array2;
for (var i = 0; i < BoardArray.length; i++) {
array2 = BoardArray[i];
for (var x = 0; x < array2.length; x++) {
if (array2[x] != "Null") {
Draw(PieceArray[array2[x]], x * 70, i * 70);
}
}
}
}