How to console.log the polygon count in WebGL, where I want the check the reference of the polygon count before culling and after culling. I'm new to WebGL where I'm analyzing the culling concepts where I want to check the count of the polygons before and culled.
Kindly, help me out! Thanks in advance.
Here's the sample code
(function(global) {
/*
* Constants, State, and Main
* www.programmingtil.com
* www.codenameparkerllc.com
*/
var KEYPRESS_SPEED = 0.2;
var IMAGES = [
{name:"stainglass", src:"/images/txStainglass.bmp"},
{name:"crate", src:"/images/txCrate.bmp"},
];
var state = {
gl: null,
mode: 'render',
ui: {
dragging: false,
mouse: {
lastX: -1,
lastY: -1,
},
pressedKeys: {},
},
animation: {},
app: {
animate: true,
eye: {
x:2.,
y:2.,
z:5.,
w:1.,
},
fog: {
color: new Float32Array([0.5,0.5,0.5]),
dist: new Float32Array([60, 80]),
on: false,
},
light: {
ambient: [0.2, 0.2, 0.2],
diffuse: [1.0, 1.0, 1.0],
position: [1.0, 2.0, 1.7],
},
objects: [],
textures: {},
},
eyeInArray: function() {
return [this.app.eye.x, this.app.eye.y, this.app.eye.z, this.app.eye.w];
}
};
glUtils.SL.init({ callback:function() { main(); } });
function main() {
state.canvas = document.getElementById("glcanvas");
state.overlay = document.getElementById("overlay");
state.ctx = state.overlay.getContext("2d");
state.gl = glUtils.checkWebGL(state.canvas, {
preserveDrawingBuffer: true,
});
initCallbacks();
initShaders();
initGL();
initCanvasTexture();
initState();
glUtils.initTextures(IMAGES, state.app.textures, function() {
draw();
if (state.app.animate) {
animate();
}
});
}
/*
* Primitives and Objects
* www.programmingtil.com
* www.codenameparkerllc.com
*/
// Create a cube
function Cube(opts) {
var opts = opts || {};
this.id = saKnife.uuid();
this.opts = opts;
this.gl = opts.gl;
this.programs = opts.programs;
// Vextex positions
// v0-v1-v2-v3 front
// v0-v3-v4-v5 right
// v0-v5-v6-v1 up
// v1-v6-v7-v2 left
// v7-v4-v3-v2 down
// v4-v7-v6-v5 back
this.attributes = {
aColor: {
size:4,
offset:0,
bufferData: new Float32Array([
1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1,
1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1,
1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1,
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1,
0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1
]),
},
aNormal: {
size:3,
offset:0,
bufferData: new Float32Array([
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0,
1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0,
-1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0,
0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0,
0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0
]),
},
aPosition: {
size:3,
offset:0,
bufferData: new Float32Array([
1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0,-1.0, 1.0, 1.0,-1.0, 1.0,
1.0, 1.0, 1.0, 1.0,-1.0, 1.0, 1.0,-1.0,-1.0, 1.0, 1.0,-1.0,
1.0, 1.0, 1.0, 1.0, 1.0,-1.0, -1.0, 1.0,-1.0, -1.0, 1.0, 1.0,
-1.0, 1.0, 1.0, -1.0, 1.0,-1.0, -1.0,-1.0,-1.0, -1.0,-1.0, 1.0,
-1.0,-1.0,-1.0, 1.0,-1.0,-1.0, 1.0,-1.0, 1.0, -1.0,-1.0, 1.0,
1.0,-1.0,-1.0, -1.0,-1.0,-1.0, -1.0, 1.0,-1.0, 1.0, 1.0,-1.0
]),
},
aSelColor: {
size:4,
offset:0,
bufferData: undefined,
},
aTexCoord: {
size:2,
offset:0,
bufferData: new Float32Array([
1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0,
0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0,
1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0,
1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0
]),
},
};
this.indices = new Uint8Array([
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9,10, 8,10,11,
12,13,14, 12,14,15,
16,17,18, 16,18,19,
20,21,22, 20,22,23
]);
// Functionality
this.readState = function() {
this.attributes.aColorBackup = this.attributes.aColor;
this.attributes.aColor = this.attributes.aSelColor;
this.state.renderMode = 'read';
};
this.drawState = function() {
this.attributes.aColor = this.attributes.aColorBackup;
this.state.renderMode = 'render';
};
this.draw = function() {
this.gl.useProgram(this.programs[this.state.renderMode]);
var uMVPMatrix = this.gl.getUniformLocation(this.programs[this.state.renderMode], 'uMVPMatrix');
var uModelMatrix = this.gl.getUniformLocation(this.programs[this.state.renderMode], 'uModelMatrix');
var uNormalMatrix = this.gl.getUniformLocation(this.programs[this.state.renderMode], 'uNormalMatrix');
var uAmbientLight = this.gl.getUniformLocation(this.programs[this.state.renderMode], 'uAmbientLight');
var uDiffuseLight = this.gl.getUniformLocation(this.programs[this.state.renderMode], 'uDiffuseLight');
var uLightPosition = this.gl.getUniformLocation(this.programs[this.state.renderMode], 'uLightPosition');
var uFogColor = this.gl.getUniformLocation(this.programs[this.state.renderMode], 'uFogColor');
var uFogDist = this.gl.getUniformLocation(this.programs[this.state.renderMode], 'uFogDist');
var uSampler0 = this.gl.getUniformLocation(this.programs[this.state.renderMode], 'uSampler0');
var mvp = state.mvp;
this.programs[this.state.renderMode].renderBuffers(this);
var n = this.indices.length;
// Model matrix
var mm = mat4.create();
if (this.opts.translate) {
mat4.translate(mm, mm, this.opts.translate);
}
if (this.opts.scale) {
mat4.scale(mm, mm, this.opts.scale);
}
if (this.state.angle[0] || this.state.angle[1] || this.state.angle[2]) {
mat4.rotateX(mm, mm, this.state.angle[0]);
mat4.rotateY(mm, mm, this.state.angle[1]);
mat4.rotateZ(mm, mm, this.state.angle[2]);
}
this.gl.uniformMatrix4fv(uModelMatrix, false, mm);
// MVP matrix
mat4.copy(mvp, state.pm);
mat4.multiply(mvp, mvp, state.vm);
mat4.multiply(mvp, mvp, mm);
this.gl.uniformMatrix4fv(uMVPMatrix, false, mvp);
// Fog
if (state.app.fog.on) {
this.gl.uniform3fv(uFogColor, state.app.fog.color);
this.gl.uniform2fv(uFogDist, state.app.fog.dist);
}
// Lighting
if (this.state.renderMode === 'render') {
this.gl.uniform3f(uDiffuseLight, state.app.light.diffuse[0], state.app.light.diffuse[1], state.app.light.diffuse[2]);
this.gl.uniform3f(uAmbientLight, state.app.light.ambient[0], state.app.light.ambient[1], state.app.light.ambient[2]);
this.gl.uniform3f(uLightPosition, state.app.light.position[0], state.app.light.position[1], state.app.light.position[2]);
var nm = mat3.normalFromMat4(mat3.create(), mm);
this.gl.uniformMatrix3fv(uNormalMatrix, false, nm);
}
// Textures
if (this.state.hasTexture) {
this.gl.activeTexture(this.gl.TEXTURE0);
this.gl.bindTexture(this.gl.TEXTURE_2D, state.app.textures[this.opts.texture]);
this.gl.uniform1i(uSampler0, 0);
}
// Blending
if (this.state.hasBlend && this.state.renderMode === 'render') {
this.gl.blendFunc(this.state.blendSrc, this.state.blendDst);
this.gl.blendEquation(this.state.blendEquation);
this.gl.disable(this.gl.CULL_FACE);
}
else {
this.gl.enable(this.gl.DEPTH_TEST);
this.gl.disable(this.gl.BLEND);
this.gl.depthMask(true);
// Culling
this.gl.enable(this.gl.CULL_FACE);
this.gl.cullFace(this.gl.BACK);
}
// Culling
// this.gl.enable(this.gl.CULL_FACE);
// this.gl.cullFace(this.gl.FRONT_AND_BACK);
// this.gl.cullFace(this.gl.FRONT);
// this.gl.cullFace(this.gl.BACK);
// Draw!
this.gl.drawElements(this.gl.TRIANGLES, n, this.gl.UNSIGNED_BYTE, 0);
};
// Cube Initialization
this.init = function(_this) {
var selColor = opts.selColor ? opts.selColor : [0,0,0,0];
_this.selColor = selColor.map(function(n) { return n/255; });
var arrays = [
_this.selColor, _this.selColor, _this.selColor, _this.selColor,
_this.selColor, _this.selColor, _this.selColor, _this.selColor,
_this.selColor, _this.selColor, _this.selColor, _this.selColor,
_this.selColor, _this.selColor, _this.selColor, _this.selColor,
_this.selColor, _this.selColor, _this.selColor, _this.selColor,
_this.selColor, _this.selColor, _this.selColor, _this.selColor,
];
_this.attributes.aSelColor.bufferData = new Float32Array([].concat.apply([], arrays));
_this.state = {
angle: opts.angle ? opts.angle : [0,0,0],
blendEquation: opts.blendEquation ? opts.blendEquation : _this.gl.FUNC_ADD,
blendSrc: opts.blendSrc ? opts.blendSrc : _this.gl.SRC_ALPHA,
blendDst: opts.blendDst ? opts.blendDst : _this.gl.ONE_MINUS_SRC_ALPHA,
hasBlend: opts.blend ? true : false,
hasTexture: opts.texture ? true : false,
mm: mat4.create(),
nm: null,
renderMode: 'render',
};
}(this);
}
/*
* Initialization
* www.programmingtil.com
* www.codenameparkerllc.com
*/
function initCallbacks() {
document.onkeydown = keydown;
document.onkeyup = keyup;
state.canvas.onmousedown = mousedown;
state.canvas.onmouseup = mouseup;
state.canvas.onmousemove = mousemove;
}
function initShaders() {
var vertexRead = glUtils.getShader(state.gl.VERTEX_SHADER, glUtils.SL.Shaders.read.vertex),
fragmentRead = glUtils.getShader(state.gl.FRAGMENT_SHADER, glUtils.SL.Shaders.read.fragment),
vertexRender = glUtils.getShader(state.gl.VERTEX_SHADER, glUtils.SL.Shaders.render.vertex),
fragmentRender = glUtils.getShader(state.gl.FRAGMENT_SHADER, glUtils.SL.Shaders.render.fragment),
vertexTex = glUtils.getShader(state.gl.VERTEX_SHADER, glUtils.SL.Shaders.tex.vertex),
fragmentTex = glUtils.getShader(state.gl.FRAGMENT_SHADER, glUtils.SL.Shaders.tex.fragment);
state.programs = {
read: glUtils.createProgram(vertexRead, fragmentRead),
render: glUtils.createProgram(vertexRender, fragmentRender),
texture: glUtils.createProgram(vertexTex, fragmentTex),
};
}
function initGL() {
state.gl.clearColor(0,0,0,1);
}
function initState() {
state.vm = mat4.create();
state.pm = mat4.create();
state.mvp = mat4.create();
state.app.objects = [
new Cube({
blend: true,
blendDst: state.gl.ONE,
gl: state.gl,
programs: {
render: state.programs.texture,
read: state.programs.read,
},
selColor:[255,254,0,0],
scale:[0.5,0.5,0.5],
texture:'stainglass',
}),
new Cube({
gl: state.gl,
programs: {
render: state.programs.render,
read: state.programs.read,
},
selColor:[255,255,0,0],
translate:[3,0,0],
}),
new Cube({
gl: state.gl,
programs: {
render: state.programs.render,
read: state.programs.read,
},
selColor:[255,253,0,0],
scale:[0.5,0.5,0.5],
translate:[-2, 2, 0],
}),
new Cube({
blend: true,
blendDst: state.gl.ONE,
gl: state.gl,
programs: {
render: state.programs.texture,
read: state.programs.read,
},
selColor:[255,252,0,0],
scale:[0.6,0.6,0.6],
texture:'crate',
translate:[-2, -2, 2],
angle: [0,35,0],
}),
new Cube({
gl: state.gl,
programs: {
render: state.programs.texture,
read: state.programs.read,
},
selColor:[255,251,0,0],
scale:[0.2,0.2,0.2],
texture:'crate',
translate:[2, 2, 2],
angle: [75,0,0],
}),
];
}
function initCanvasTexture() {
var texture = state.gl.createTexture();
var textCanvas = document.createElement('canvas');
textCanvas.width = 256;
textCanvas.height = 256;
var ctx = textCanvas.getContext('2d');
// Setup background
ctx.fillStyle = 'rgba(53, 60, 145, 1.0)';
ctx.fillRect(0, 0, textCanvas.width, textCanvas.height);
// Setup font
ctx.font = '36px bold sans-serif';
ctx.fillStyle = 'rgba(0, 60, 145, 1.0)';
ctx.textBaseline = 'middle';
ctx.shadowColor = 'rgba(10, 160, 190, 1.0)';
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.shadowBlur = 5;
// Draw out some text
var text = 'ProgrammingTIL';
var textWidth = ctx.measureText(text).width;
ctx.fillText(text, (textCanvas.width-textWidth)/2, textCanvas.height/2);
// Change the font and draw out more text
ctx.font = '26px bold sans-serif';
ctx.fillStyle = 'white';
ctx.shadowColor = 'rgba(0, 0, 0, 0)';
text = 'David';
textWidth = ctx.measureText(text).width;
ctx.fillText(text, (textCanvas.width-textWidth)/2, textCanvas.height/2 - 60);
text = 'Parker';
textWidth = ctx.measureText(text).width;
ctx.fillText(text, (textCanvas.width-textWidth)/2, textCanvas.height/2 + 60);
// Put the canvas onto the texture object
state.gl.pixelStorei(state.gl.UNPACK_FLIP_Y_WEBGL, 1);
state.gl.bindTexture(state.gl.TEXTURE_2D, texture);
state.gl.texImage2D(state.gl.TEXTURE_2D, 0, state.gl.RGBA, state.gl.RGBA, state.gl.UNSIGNED_BYTE, textCanvas);
state.gl.texParameteri(state.gl.TEXTURE_2D, state.gl.TEXTURE_MAG_FILTER, state.gl.LINEAR);
state.gl.texParameteri(state.gl.TEXTURE_2D, state.gl.TEXTURE_MIN_FILTER, state.gl.LINEAR);
state.gl.texParameteri(state.gl.TEXTURE_2D, state.gl.TEXTURE_WRAP_S, state.gl.CLAMP_TO_EDGE);
state.gl.texParameteri(state.gl.TEXTURE_2D, state.gl.TEXTURE_WRAP_T, state.gl.CLAMP_TO_EDGE);
state.app.textures['canvastext'] = texture;
}
/*
* State Management
* www.programmingtil.com
* www.codenameparkerllc.com
*/
function updateOverlay() {
var msg = "Eye position: ("+state.app.eye.x.toFixed(2)+","+state.app.eye.y.toFixed(2)+","+state.app.eye.z.toFixed(2)+")";
state.ctx.clearRect(0,0,state.ctx.canvas.width,state.ctx.canvas.height);
state.ctx.save();
state.ctx.font = "20px Helvetica";
state.ctx.fillStyle = "white";
state.ctx.fillText(msg, 10, 25);
state.ctx.restore();
}
function updateState() {
if (state.ui.pressedKeys[37]) {
// left
state.app.eye.x += KEYPRESS_SPEED;
} else if (state.ui.pressedKeys[39]) {
// right
state.app.eye.x -= KEYPRESS_SPEED;
} else if (state.ui.pressedKeys[40]) {
// down
state.app.eye.y += KEYPRESS_SPEED;
} else if (state.ui.pressedKeys[38]) {
// up
state.app.eye.y -= KEYPRESS_SPEED;
} else if (state.ui.pressedKeys[90] && !state.ui.pressedKeys[16]) {
// z
state.app.eye.z += KEYPRESS_SPEED;
} else if (state.ui.pressedKeys[90] && state.ui.pressedKeys[16]) {
// Shift+z
state.app.eye.z -= KEYPRESS_SPEED;
}
}
/*
* Rendering / Drawing / Animation
* www.programmingtil.com
* www.codenameparkerllc.com
*/
function animate() {
state.animation.tick = function() {
updateOverlay();
updateState();
draw();
requestAnimationFrame(state.animation.tick);
};
state.animation.tick();
}
function draw() {
state.gl.clear(state.gl.COLOR_BUFFER_BIT | state.gl.DEPTH_BUFFER_BIT);
mat4.perspective(state.pm,
20, state.canvas.width/state.canvas.height, 1, 100
);
mat4.lookAt(state.vm,
vec3.fromValues(state.app.eye.x,state.app.eye.y,state.app.eye.z),
vec3.fromValues(0,0,0),
vec3.fromValues(0,1,0)
);
// Note: First you should sort (transparent) objects based on distance -> furthest away first
// For our purposes, we'll loop through everything twice. Once to draw opaque objects
// and another for transparent objects.
state.gl.enable(state.gl.DEPTH_TEST);
state.gl.disable(state.gl.BLEND);
state.gl.depthMask(true);
state.app.objects.forEach(function(obj) {
if (!obj.state.hasBlend) {
obj.draw();
}
});
// Leave on the depth test!
// state.gl.disable(state.gl.DEPTH_TEST);
state.gl.enable(state.gl.BLEND);
state.gl.depthMask(false);
state.app.objects.forEach(function(obj) {
if (obj.state.hasBlend) {
obj.draw();
}
});
state.gl.depthMask(true);
}
/*
* UI Events
* www.programmingtil.com
* www.codenameparkerllc.com
*/
function keydown(event) {
state.ui.pressedKeys[event.keyCode] = true;
}
function keyup(event) {
state.ui.pressedKeys[event.keyCode] = false;
}
function mousedown(event) {
if (uiUtils.inCanvas(event)) {
state.gl.disable(state.gl.BLEND);
state.gl.enable(state.gl.DEPTH_TEST);
state.gl.depthMask(true);
state.app.objects.forEach(function(obj) {
obj.readState();
draw();
});
var pixels = Array.from(uiUtils.pixelsFromMouseClick(event, state.canvas, state.gl));
var obj2 = uiUtils.pickObject(pixels, state.app.objects, 'selColor');
if (obj2) {
state.app.objSel = obj2;
state.ui.mouse.lastX = event.clientX;
state.ui.mouse.lastY = event.clientY;
state.ui.dragging = true;
}
state.gl.enable(state.gl.BLEND);
state.gl.disable(state.gl.DEPTH_TEST);
state.gl.depthMask(false);
state.app.objects.forEach(function(obj) {
obj.drawState();
draw();
});
}
}
function mouseup(event) {
state.ui.dragging = false;
}
function mousemove(event) {
var x = event.clientX;
var y = event.clientY;
if (state.ui.dragging) {
// The rotation speed factor
// dx and dy here are how for in the x or y direction the mouse moved
var factor = 10/state.canvas.height;
var dx = factor * (x - state.ui.mouse.lastX);
var dy = factor * (y - state.ui.mouse.lastY);
// update the latest angle
state.app.objSel.state.angle[0] = state.app.objSel.state.angle[0] + dy;
state.app.objSel.state.angle[1] = state.app.objSel.state.angle[1] + dx;
}
// update the last mouse position
state.ui.mouse.lastX = x;
state.ui.mouse.lastY = y;
}
})(window || this);
WebGL has no way to give you the info you seek.
WebGL just looks at clipspace triangles in 2D (the values your shader writes to gl_Position). In that 2D space it labels them clockwise or counter-clockwise and then discards triangles that you requested to be culled based on the culling settings.
In order to know how many triangles are drawn (or cullled) you'd need to take the math happening in your shaders related to gl_Position and reproduce that math in JavaScript. Then run your data through that math and generate triangle vertices. For any triangle you compute the area
--pseudo code--
area = 0;
for (let i = 0; i < 3; ++i) {
p0 = threeScreenSpaceTriangleVertices[i];
p1 = threeScreenSpaceTriangleVertices[(i + 1) % 3];
area += p0.x * p1.y - p1.x * p0.y;
}
area *= 0.5; // not really important
If area is positive it's clockwise, if it's negative it's counter clockwise
I guess it's not possible out of the box with WebGL.
Face culling (in your case backface culling) works by comparing the orientation of the face with the looking direction of the camera.
Thus you can easily write some code on CPU/JS side to compute the number of faces displayed when culling is enabled.
To do so, create at render time a loop over all your cubes faces. Get the normal vectors of the face, transform it with the normal matrix (world inverse transposed) and the view matrix and finally normalize it. At this point you have the normal vector transformed in eye space.
Then compute the dot product with the eye looking vector (0,0,-1) (already normalized). The result is the cosine of the angle between normal and camera.
If the sign is negative, the camera is looking at the front of the face and you can increase the draw count. It it's positive, the camera sees the back of the face and you can discard this one.
// In pseudo GLSL code (write it on JS side)
vec3 normalEyeSpace = normalize(viewMatrix * worldInverseTranspose * aNormal)
float dir = dot(normalEyeSpace, vec3(0.0, 0.0, -1.0)
if (dir < 0) drawCount++
I was wondering if someone could help me find the solution to this.
I've made a very simple animation using KineticJS.
All works perfect on desktop, unfortunately not on mobile devices (iPhone, iPad, Android).
Result is a slowish performance but most importantly distorted shapes.
I suspect it has something to do with resolution or viewport but am not sure.
Preview is on www.bartvanhelsdingen.com
Any suggestions are highly appreciated.
Below is the code:
var shapes = {
sizes: [30, 40, 50, 55, 60, 80],
gradients: [
[0, '#fdfaee', 1, '#524f43'],
[0, '#a39175', 1, '#dbae5e'],
[0, '#b4c188', 1, '#f3de7c'],
[0, '#eaf2ef', 1, '#587c71'],
[0, '#a39175', 1, '#dbae5e'],
[0, '#61845c', 1, '#b4b092']
],
},
dims = {
width: 300,
height: 500
},
stage = new Kinetic.Stage({
container: 'animation',
width: dims.width,
height: dims.height,
x: 0,
y: 0,
draggable: false
});
function getRandomColor() {
return colors[getRandomFromInterval(0, colors.length - 1)];
}
function getRandomGradient() {
return gradients[getRandomFromInterval(0, gradients.length - 1)];
}
function getRandomFromInterval(from, to) {
return Math.floor(Math.random() * (to - from + 1) + from);
}
function getRandomSpeed() {
var speed = getRandomFromInterval(1, 1);
return getRandomFromInterval(0, 1) ? speed : speed * -1;
}
function createGroup(x, y, size, strokeWidth) {
return new Kinetic.Group({
x: x,
y: y,
width: size,
height: size,
opacity: 0,
draggable: false,
clipFunc: function (canvas) {
var context = canvas.getContext();
context.beginPath();
context.moveTo(0, 0);
context.lineTo(0, size);
context.lineTo(size, size);
context.lineTo(size, 0);
context.rect(strokeWidth, strokeWidth, size - strokeWidth * 2, size - strokeWidth * 2);
}
});
}
function createShape(size, gradient, strokeWidth, cornerRadius) {
return new Kinetic.Rect({
x: 0,
y: 0,
width: size,
height: size,
fillLinearGradientStartPoint: [size, 0],
fillLinearGradientEndPoint: [size, size],
fillLinearGradientColorStops: gradient,
opacity: 1,
lineJoin: 'bevel',
strokeWidth: 0,
cornerRadius: cornerRadius
});
}
var layer = new Kinetic.Layer(),
animAttribs = [];
for (var n = 0; n < 6; n++) {
var size = shapes.sizes[n],
strokeWidth = Math.ceil(size * 0.12),
cornerRadius = Math.ceil(size * 0.04),
gradient = shapes.gradients[n],
x = getRandomFromInterval(size, dims.width) - size,
y = getRandomFromInterval(size, dims.height) - size;
var group = createGroup(x, y, size, strokeWidth);
var shape = createShape(size, gradient, strokeWidth, cornerRadius);
animAttribs.push({
nextChange: getRandomFromInterval(1, 3) * 1000,
startTime: 1000,
duration: 0,
x: getRandomSpeed(),
y: getRandomSpeed()
});
group.add(shape);
layer.add(group);
}
stage.add(layer);
anim = new Kinetic.Animation(function (frame) {
var time = frame.time,
timeDiff = frame.timeDiff,
frameRate = frame.frameRate;
for (var n = 0; n < layer.getChildren().length; n++) {
var shape = layer.getChildren()[n],
opacity = shape.getOpacity() + 0.01 > 1 ? 1 : shape.getOpacity() + 0.01,
attribs = animAttribs[n],
x, y;
if (attribs.duration >= attribs.nextChange) {
attribs.x = getRandomSpeed();
attribs.y = getRandomSpeed();
attribs.nextChange = getRandomFromInterval(3, 5) * 1000;
attribs.duration = 0;
}
if (time >= attribs.startTime) {
if (shape.getX() + attribs.x + shape.getWidth() >= stage.getWidth() || shape.getX() + attribs.x - shape.getWidth() / 2 <= 0) {
attribs.x *= -1;
}
if (shape.getY() + attribs.y + shape.getHeight() >= stage.getHeight() || shape.getY() + attribs.y - shape.getHeight() / 2 <= 0) {
attribs.y *= -1;
}
x = shape.getX() + attribs.x;
y = shape.getY() + attribs.y;
attribs.duration += timeDiff;
shape.setOpacity(opacity);
shape.setX(x);
shape.setY(y);
}
}
}, layer);
anim.start();
the problem you are facing is, that clipFunc isn't currently working on devices with pixelratio != 1.
This problem came up in this post as well. Eric Rowell, the creator of KineticJS added this issue to his release scedule for late September.
So there is nothing wrong with your animations, they're working as expected, but you can't see them because of the distorted clipping region
To resolve this issue "unofficially" you can simply replace the last line of the _clip function in your kinetic.js with the following: context.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); (credits for that go to Mark Smits)