With konvajs I'm having problems to render many objects, more than 1000. I already tried all performance tips they mentioned but stills seems to be a bit slow. Then I made this basic comparison and I have these numbers. Should I migrate my code to fabricjs?. Improvements and suggestions are please welcome!!
I took that code and customized a little: https://github.com/jonobr1/two.js/issues/237
(function() {
fabric.Object.prototype.transparentCorners = false;
// this.__canvases = [];
//
// General parameters\
//
const objectsNumber = 1000;
const objectsRadius = 15;
const canvasWidth = 390;
const canvasHeight = 390;
var i, dot, circle,
t1, t2,
// only to have
startTimer = function() {
t1 = new Date().getTime();
return t1;
},
stopTimer = function() {
t2 = new Date().getTime();
return t2 - t1;
},
// getRandomInt = fabric.util.getRandomInt,
rainbow = ["#ffcc66", "#ccff66", "#66ccff", "#ff6fcf", "#ff6666"],
rainbowEnd = rainbow.length - 1;
//
// Rendering canvas #1 twojs
//
var elem = document.getElementById('c1');
var two = new Two({
width: 390,
height: 390
}).appendTo(elem);
results1 = document.getElementById('results-c1');
startTimer();
for (i = 1000; i >= 0; i--) {
circle = two.makeCircle(Math.floor(Math.random() * 400) + 0, Math.floor(Math.random() * 350) + 0, objectsRadius);
circle.fill = rainbow[Math.floor(Math.random() * rainbowEnd) + 0];
circle.noStroke();
circle.opacity = 0.75;
}
two.update();
results1.innerHTML = 'Twojs rendering of 1000 elements in ' + stopTimer() + 'ms';
//
// Rendering canvas #2
//
var canvas2 = new fabric.Canvas('c2', {
backgroundColor: "white",
renderOnAddRemove: false
});
canvas2.setHeight(390);
canvas2.setWidth(390);
var results2 = document.getElementById('results-c2');
startTimer();
for (i = 1000; i >= 0; i--) {
dot = new fabric.Circle({
left: Math.floor(Math.random() * 400) + 0,
top: Math.floor(Math.random() * 350) + 0,
radius: objectsRadius,
fill: rainbow[Math.floor(Math.random() * rainbowEnd) + 0],
objectCaching: false,
opacity: 0.75
});
canvas2.add(dot);
}
canvas2.renderAll(); // Note, calling renderAll() is important in this case
results2.innerHTML = 'Fabricjs rendering 1000 elements using canvas.renderOnAddRemove = false and objectCaching = false in ' + stopTimer() + 'ms';
// this.__canvases.push(canvas2);
//
// Rendering canvas #3 konvajs
//
var stage = new Konva.Stage({
container: 'c3',
width: 390,
height: 390
});
var layer = new Konva.Layer();
results3 = document.getElementById('results-c3');
startTimer();
for (i = 1000; i >= 0; i--) {
var kcircle = new Konva.Circle({
x: Math.floor(Math.random() * 400) + 0,
y: Math.floor(Math.random() * 350) + 0,
radius: objectsRadius,
fill: rainbow[Math.floor(Math.random() * rainbowEnd) + 0],
draggable: true,
opacity: 0.75
});
layer.add(kcircle);
}
stage.add(layer);
results3.innerHTML = 'Konvajs rendering of 1000 elements in ' + stopTimer() + 'ms';
})();
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.7.0/fabric.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/two.js/0.6.0/two.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/3.2.3/konva.min.js"></script>
<div class="row">
<div id="results-c1" class=" col-lg-4"></div>
<div id="results-c2" class=" col-lg-4"></div>
<div id="results-c3" class=" col-lg-4"></div>
</div>
<div class="row">
<div id="c1" class=" col-lg-4"></div>
<canvas id="c2" class=" col-lg-4" width="390" height="390"></canvas>
<div id="c3" class=" col-lg-4"></div>
</div>
Related
I wanna do some effect to my background so i tought i can add some shapes flying around but i do only 1 shape i cant loop or anything
i tried call function more than a one time but it doesnt help
function animasyon(a) {
window.onload=function(){
var id = setInterval(anim,5);
$('body').append('<div class=shape></div>');
$('body').append('<div class=shape></div>');
var kutu = document.getElementsByClassName("shape");
var pos = 0;
var x = window.innerWidth;
var y = window.innerHeight;
var borderSize = Math.floor(Math.random() * 3 ) + 1;
var ySize = Math.floor(Math.random() * y ) + 1;
var Size = Math.floor(Math.random() * 30 ) + 5;
var yon = Math.floor(Math.random() *2)+1;
var dolu = Math.floor(Math.random() *2)+1;
if (ySize > 50) { ySize-=20; }
function anim(){
if (pos == x) {
clearInterval(id);
document.getElementById("shape").remove();
}else{
pos++;
kutu[a].style.position = "absolute";
kutu[a].style.border = "solid rgb(119,38,53) "+borderSize+"px";
kutu[a].style.left = pos+"px";
kutu[a].style.width = Size+"px";
kutu[a].style.height = Size+"px";
if (yon == 1) { ySize-=0.2; } else { ySize+=0.2; }
if (dolu==1) {kutu[a].style.background = "rgb(119,38,53)";}
if (kutu[a].offsetTop < 0 || kutu[a].offsetTop > y-30) {document.getElementById("shape").remove();}
kutu[a].style.top = ySize+"px";
}
}
}
}
animasyon(0);
Try Calling 'anim' function in this way
setInterval(function(){anim()},5);
Your problem is that you are assigning window.onload function a value inside the function animasyon
window.onload holds only 1 function. If you call animation function more than once, then the last one will overwrite the first one.
Edit: You need to separate your animation logic from the page load logic. They are completely separate things.
Here is an example of adding multiple objects and animating each separately.
// HTML
<div id="container">
<div id="ball"></div>
<div id="ball2"></div>
<div id="ball3"></div>
<div id="ball4"></div>
</div>
// CSS
#ball, #ball2, #ball3, #ball4 {
background: red;
border: 1px solid #FAFDFA;
display: inline-block;
width: 1em;
height: 1em;
border-radius: 2em;
position: absolute;
}
#ball2 {
background: blue;
}
#ball3 {
background: green;
}
#ball4 {
background: purple;
}
#container {
width: 512px;
height: 512px;
background: black;
position: relative;
}
// JS
const container = document.getElementById('container');
const stageWidth = 512;
const stageHeight = 512;
const makeFall = (elementId) => {
// this function makes an enlement fall in random direction
const animationSpeed = 4;
// your onload function
const ball = document.getElementById(elementId);
let directionX = (Math.random() * animationSpeed * 2) - animationSpeed;
let directionY = Math.random() * animationSpeed;
const setRandomStart = () => {
ball.style.top = '10px';
ball.style.left = (Math.random() * (stageWidth / 2)) + 'px';
directionX = (Math.random() * animationSpeed * 2) - animationSpeed;
directionY = Math.random() * animationSpeed;
}
setRandomStart();
let animationInterval = setInterval(() => {
let px = parseFloat(ball.style.left);
let py = parseFloat(ball.style.top);
px += directionX;
py += directionY;
if (px > stageWidth - 20 || py > stageHeight - 20 || px < -20) {
setRandomStart();
} else {
ball.style.left = px + 'px';
ball.style.top = py + 'px';
}
}, 48);
}
// In Your onload function you can add the elements and then animate
makeFall('ball');
makeFall('ball2');
makeFall('ball3');
makeFall('ball4');
https://jsfiddle.net/753oL8re/4/
I have some chart what have two colors (Red and Blue).
Example
.
I use Chartjs
How it most work:
I have values, for example:
red from 1 to 350,
blue from 351 to 1000
Warning: Values (red and blue) can changes.
Ok, we have values, now I wont rotate my chart X counts and stop on value 256, (this is red color).
(start on top - Place of Start)
Q: How I can do this?
For example, I find some code what random spin (rotate) my chart:
var spin = function () {
var timeOut = function () {
// called by setTimeout to update the chart after rotation been changed
dynChart.update();
}
// generate random angle
var newAngle = Math.random() * 1080 + currAngle;
for (var angle = currAngle, interval = 100; angle < newAngle; angle += 1, interval += 5) {
dynChart.options.rotation = angle;
setTimeout(timeOut, interval);
}
//currAngle = dynChart.options.rotation;
}
This could help you:
var config = {
type: 'pie',
data: {
datasets: [{
data: [
300,
700
],
backgroundColor: [
window.chartColors.red,
window.chartColors.blue,
],
label: 'Dataset 1'
}]
},
options: {
responsive: true,
rotation: 0,
animation: {
duration: 2000
}
}
};
var myPie;
window.onload = function() {
create();
};
function create() {
var ctx = document.getElementById('chart-area').getContext('2d');
config.data.datasets[0].data = [$("#redValue").val(), $("#blueValue").val()]
config.options.rotation = -Math.PI / 2;
config.options.animation.duration = 2000;
myPie = new Chart(ctx, config);
// console.log(myPie);
}
function rotate() {
var fromDegree = 0;
var toDegree = 360 * (parseFloat($("#stopValue").val())) /
(parseFloat($("#redValue").val()) + parseFloat($("#blueValue").val()));
rotatePie(0, 360, 8,
function() {
rotatePie(0, 360, 15,
function() {
rotatePie(fromDegree, toDegree, 30,
function() {})
})
});
}
function rotatePie(fromDegree, toDegree, delay, callback) {
var rotation = -Math.PI / 2 - fromDegree * 2 * Math.PI / 360;
var stopRotation = -Math.PI / 2 - toDegree * 2 * Math.PI / 360;
var intervalId = setInterval(frame, delay);
function frame() {
if (rotation <= stopRotation) {
console.log('2');
clearInterval(intervalId);
callback();
} else {
rotation = rotation - Math.PI * 0.03;
myPie.config.options.rotation = rotation;
myPie.config.options.animation.duration = 0;
myPie.update();
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://www.chartjs.org/samples/latest/utils.js"></script>
<script src="https://www.chartjs.org/dist/2.7.3/Chart.bundle.js"></script>
<div id="canvas-holder" style="width:40%; margin: 20px">
<canvas id="chart-area"></canvas>
</div>
Red Value: <input id="redValue" type="number" value="300" />
<br/> Blue Value: <input id="blueValue" type="number" value="700" />
<br/>
<button type="button" onclick="create()">Create</button>
<br/> <br/> Stop On: <input id="stopValue" type="number" value="350" />
<br/><br/>
<button type="button" onclick="rotate()">Rotate</button>
Below is a KonvaJS project where you can add stickers to an image. However, it has a fixed width and a fixed height.
Now because the sizes are fixed, it won't work with anything response, like a bootstrap modal.
Here is my attempt following a KonvaJS response guide, see here. and the guide here.
In my attempt, after I upload the image, my code can't get the new width of the modal as it returns 0, so it can't calculate it for the size of the canvas.
How can I make the canvas responsive?
function centreRectShape(shape) {
shape.x((stage.getWidth() - shape.getWidth()) / 2);
shape.y((stage.getHeight() - shape.getHeight()) / 2);
}
var stage = new Konva.Stage({
container: 'canvas-container',
width: 650,
height: 300
});
var layer = new Konva.Layer();
stage.add(layer);
var bgRect = new Konva.Rect({
width: stage.getWidth(),
height: stage.getHeight(),
fill: 'gold',
opacity: 0.1
});
layer.add(bgRect);
var uploadedImage = new Konva.Image({
draggable: false
});
layer.add(uploadedImage);
// make an object to keep things tidy - not strictly needed, just being tidy
function addSticker(imgUrl){
// make the sticker image object
var stickerObj = new Konva.Image({
x: 240,
y: 20,
width: 93,
height: 104,
name: 'sticker',
draggable: true
});
layer.add(stickerObj);
// make the sticker image loader html element
var stickerImage = new Image();
stickerImage.onload = function() {
stickerObj.image(stickerImage);
layer.draw();
};
stickerObj.on('transformstart', function(){
undoBefore = makeUndo(this);
})
stickerObj.on('transformend', function(){
var undoAfter = makeUndo(this);
addUndo(123, undoBefore, undoAfter)
})
// assigning the URL of the image starts the onload
stickerImage.src = imgUrl;
}
imgObj = new Image();
imgObj.onload = function() {
uploadedImage.image(imgObj);
var padding = 20;
var w = imgObj.width;
var h = imgObj.height;
var targetW = stage.getWidth() - (2 * padding);
var targetH = stage.getHeight() - (2 * padding);
var widthFit = targetW / w;
var heightFit = targetH / h;
var scale = (widthFit > heightFit) ? heightFit : widthFit;
w = parseInt(w * scale, 10);
h = parseInt(h * scale, 10);
uploadedImage.size({
width: w,
height: h
});
centreRectShape(uploadedImage);
layer.draw();
}
imgObj.src = 'https://images.pexels.com/photos/787961/pexels-photo-787961.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260';
$('.sticker').on('click', function() {
var theSticker = addSticker($(this).attr('src'));
toggle(true);
toggle(false);
});
var vis = false;
$('#toggler').on('click', function(){
toggle(vis);
})
function undoData(opts){
this.x = opts.x;
this.y = opts.y;
this.width = opts.w;
this.height = opts.h;
this.rotation = opts.r;
}
var undoBefore;
function makeUndo(shape){
return new undoData({x:shape.getX(), y: shape.getY(), w: shape.getWidth(), h: shape.getHeight(), r: shape.getRotation() })
}
var undoList = [];
function addUndo(shapeId, before, after){
undoList.push({id: shapeId, before: before, after: after});
console.log(undoList[undoList.length - 1])
}
function toggle(isVisible){
if (!isVisible){
var shapes = stage.find('.sticker');
shapes.each(function(shape) {
var imgRotator = new Konva.Transformer({
node: shape,
name: 'stickerTransformer',
keepRatio: true,
enabledAnchors: ['top-left', 'top-right', 'bottom-left', 'bottom-right']
});
layer.add(imgRotator);
})
vis = true;
}
else {
var shapes = stage.find('.stickerTransformer');
shapes.each(function(shape) {
shape.remove();
})
vis=false;
}
layer.draw();
$('#toggler').html((vis ? 'Toggle Off' : 'Toggle On'));
}
html,
* {
margin: 0;
padding: 0;
}
body {
background: #eee;
}
#image-editor {
background: #fff;
border-radius: 3px;
border: 1px solid #d8d8d8;
width: 650px;
margin: 0 auto;
margin-top: 20px;
box-shadow: 0 3px 5px rgba(0, 0, 0, .2);
}
.stickers {
padding: 10px 5px;
background: #eee;
}
.stickers>img {
margin-right: 10px;
}
<div id="image-editor">
<div id="canvas-container"></div>
<div class="stickers">
<img class="sticker" src="https://craftblock.me/koa/fb-upload-clone/stickers/sticker%20(1).png" alt="Sticker" width="62px">
</div>
</div>
<script src="https://unpkg.com/konva#2.4.1/konva.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
So let me try to explain the issue a bit more: Check out the live version of my attempt of making it responsive.
As you can see, after trying to load the image into the canvas, the modal pops up but the canvas fails to resize.
Here's the JS to that:
/**
* Image Editor
*/
var stageWidth = 1000;
var stageHeight = 1000;
var stage = new Konva.Stage({
container: 'canvas-container',
width: stageWidth,
height: stageHeight
});
var layer = new Konva.Layer();
stage.add(layer);
var bgRect = new Konva.Rect({
width: stage.getWidth(),
height: stage.getHeight(),
fill: 'gold',
opacity: 0.1
});
layer.add(bgRect);
var uploadedImage = new Konva.Image({
draggable: false
});
layer.add(uploadedImage);
imgObj.onload = function () {
uploadedImage.image(imgObj);
var padding = 20;
var w = imgObj.width;
var h = imgObj.height;
var targetW = stage.getWidth() - (2 * padding);
var targetH = stage.getHeight() - (2 * padding);
var widthFit = targetW / w;
var heightFit = targetH / h;
var scale = (widthFit > heightFit) ? heightFit : widthFit;
w = parseInt(w * scale, 10);
h = parseInt(h * scale, 10);
uploadedImage.size({
width: w,
height: h
});
centreRectShape(uploadedImage);
layer.draw();
}
$('.sticker').on('click', function () {
addSticker($(this).attr('src'));
});
fitStageIntoParentContainer();
window.addEventListener('resize', fitStageIntoParentContainer);
function centreRectShape(shape) {
shape.x((stage.getWidth() - shape.getWidth()) / 2);
shape.y((stage.getHeight() - shape.getHeight()) / 2);
}
function addSticker(imgUrl) {
var stickerObj = new Konva.Image({
x: 240,
y: 20,
width: 93,
height: 104,
draggable: true
});
var stickerImage = new Image();
stickerImage.onload = function () {
stickerObj.image(stickerImage);
centreRectShape(stickerObj);
layer.draw();
};
stickerImage.src = imgUrl;
layer.add(stickerObj);
addModifiers(stickerObj);
}
function addModifiers(obj) {
var imgRotator = new Konva.Transformer({
node: obj,
keepRatio: true,
enabledAnchors: ['top-left', 'top-right', 'bottom-left', 'bottom-right']
});
layer.add(imgRotator);
}
function fitStageIntoParentContainer() {
var container = document.querySelector("edit-image-modal");
// now we need to fit stage into parent
var containerWidth = container.offsetWidth;
// to do this we need to scale the stage
var scale = containerWidth / stageWidth;
stage.width(stageWidth * scale);
stage.height(stageHeight * scale);
stage.scale({
x: scale,
y: scale
});
stage.draw();
}
The technique you have used to listen for 'resize' on the page will work for the main window but likely not for the modal. You can confirm that by some simple console.log() output.
You need to use the bootstrap event on('show.bs.modal') to catch when the modal is shown, which is when you really want to fire the call to fitStageIntoParentContainer();
See this SO post for info. It is not a duplicate but covers the bootstrap modal event.
In case that question is erased, you should be heading for something like:
$('your_modal_element_selector').on('show.bs.modal', function () {
fitStageIntoParentContainer();
});
I am new in Fabric js. can anyone suggest me for restrict scale object within bounding box ?
my java-script code is below
(function(global) {
var canvas = new fabric.Canvas('maincanvas');
var yourNameObjs = [];
var goodtop =358, goodleft=250, boundingObject;
var boundingObject = new fabric.Rect({
left: 100,
top: 90,
width: 200,
height: 250,
opacity: 4,
selectable:false,
fill: 'red',
});
canvas.add(boundingObject);
addText = function(){
var nametoprint = $("#nametoprint").val();
canvas.remove(yourNameObjs);
yourNameObjs = new fabric.Text(nametoprint, {
left: 150, //Take the block's position
opacity: 9,
top: 150,
fontFamily: 'Delicious_500',
}
);
canvas.add(yourNameObjs);
canvas.on("object:moving", function(){
var obj = yourNameObjs;
var bounds = boundingObject;
obj.setCoords();
if(!obj.isContainedWithinObject(bounds)){
obj.setTop(goodtop);
obj.setLeft(goodleft);
} else {
goodtop = obj.top;
goodleft = obj.left;
}
});
canvas.on("object:scaling", function(){
var obj = yourNameObjs;
var bounds = boundingObject;
obj.setCoords();
if(!obj.isContainedWithinObject(bounds)){
obj.setTop(goodtop);
obj.setLeft(goodleft);
} else {
goodtop = obj.top;
goodleft = obj.left;
}
});
canvas.renderAll();
};
})();
html code is below
<input type="text" name="nametoprint" id="nametoprint" value="alex" />
<input type= "button" name="addtxt" id="addtxt" onclick="addText()" value="add text" />
<canvas id="maincanvas" style="border:1px solid #000;" width="400" height="450" ></canvas>
also i added this in fiddle here http://jsfiddle.net/pfgm8myp/
i want only allow to scale text object within the red bound box.moving object is working fine.i also added code for scaling but its not working.
can any one suggest me how to restrict this scaling ?
thanks in advance.
i posted it here with my own logic. http://jsfiddle.net/9xojfmyt/
i added below javascript code
(function(global) {
var canvas = new fabric.Canvas('maincanvas');
var yourNameObjs = [];
var goodtop =358, goodleft=250, boundingObject;
var boundingObject = new fabric.Rect({
left: 100,
top: 90,
width: 200,
height: 250,
opacity: 4,
selectable:false,
fill: 'red',
});
canvas.add(boundingObject);
addText = function(){
var nametoprint = $("#nametoprint").val();
canvas.remove(yourNameObjs);
yourNameObjs = new fabric.Text(nametoprint, {
left: 150, //Take the block's position
opacity: 9,
top: 150,
fontFamily: 'Delicious_500',
}
);
canvas.add(yourNameObjs);
// canvas moving limit
canvas.observe("object:moving", function(e){
var obj = yourNameObjs;
var bounds = boundingObject;
var objw = parseInt(parseInt(obj.width) * obj.scaleX);
var objh = parseInt(parseInt(obj.height) * obj.scaleY);
//left
if(obj.left < bounds.left){
obj.setLeft(bounds.left);
}
//top
if(obj.top < bounds.top){
obj.setTop(bounds.top);
}
//right
if((parseInt(obj.left) + objw) > (parseInt(bounds.left)+parseInt(bounds.width))){
obj.setLeft(((parseInt(bounds.left)+parseInt(bounds.width)) - objw));
}
//bottom
if((parseInt(obj.top) + objh) > (parseInt(bounds.top)+parseInt(bounds.height))){
obj.setTop(((parseInt(bounds.top)+parseInt(bounds.height)) - objh));
}
});
// canvas scaling limit
canvas.observe("object:scaling", function(e){
var obj = yourNameObjs;
var bounds = boundingObject;
var objw = parseInt(parseInt(obj.width) * obj.scaleX);
var objh = parseInt(parseInt(obj.height) * obj.scaleY);
//left
if(obj.left < bounds.left || (parseInt(obj.left) + objw) > (parseInt(bounds.left)+parseInt(bounds.width))){
obj.setLeft(bounds.left);
obj.setScaleX((bounds.width/obj.width));
}
//top
if(obj.top < bounds.top || (parseInt(obj.top) + objh) > (parseInt(bounds.top)+parseInt(bounds.height))){
obj.setTop(bounds.top);
obj.setScaleY((bounds.height/obj.height));
}
});
canvas.renderAll();
};
})();
Bit modification compare to Albert's answer. If you want to restrict object within canvas then replace moving event with below code.
// canvas scaling limit
canvas.observe("object:scaling", function(e){
var obj = e.target;
var bounds = {
left : 5, //here it keep 5 px margin from all direction
top : 5,
width : canvas.width - 10,
height : canvas.height - 10
};
var objw = parseInt(parseInt(obj.width) * obj.scaleX);
var objh = parseInt(parseInt(obj.height) * obj.scaleY);
//left
if(obj.left < bounds.left || (parseInt(obj.left) + objw) > (parseInt(bounds.left)+parseInt(bounds.width))){
obj.setLeft(bounds.left);
obj.setScaleX((bounds.width/obj.width));
}
//top
if(obj.top < bounds.top || (parseInt(obj.top) + objh) > (parseInt(bounds.top)+parseInt(bounds.height))){
obj.setTop(bounds.top);
obj.setScaleY((bounds.height/obj.height));
}
});
So im attempting to use lulu's CCV JavaScript library and this code example seen here that draws glasses over a detected face: enter link description here
But instead of drawing glasses I would like to be able to detect the face, and draw a rectangle around it, then save it in the web browser as a variable.
In the face.html file there is a line which I believe draws the glasses to the detected face
for (i = App.comp.length; i--; ) {
ctx.strokeRect(App.glasses, (App.comp[i].x - w / 2) * m, (App.comp[i].y - w / 2) * m, (App.comp[i].width + w) * m, (App.comp[i].height + w) * m);
}
}
};
I have tried replaced that line with this code snippet:
ctx.strokeRect(comp[i].x, comp[i].y, comp[i].width, comp[i].height);
It does draw a rectangle, but a small one in the corner of the screen. I'm lost lost as where to go from here.
The entire code for the html page is seen here:
<pre>
<!DOCTYPE html>
<html lang="en">
<!-- Adapted to work with the getUserMedia API using code from http://wesbos.com/html5-video-face-detection-canvas-javascript/ -->
<head>
<meta charset="utf-8">
<title>HTML5 Face Detection - JavaScript getUserMedia API and Groucho Marx glasses!</title>
<style>
body {
font-family: sans-serif;
font-size: 17px;
line-height: 24px;
color: #fff;
width: 100%;
height: 100%;
margin: 0;
text-align: center;
background-color: #111;
}
#info {
position: absolute;
width: 100%;
height: 30px;
top: 50%;
margin-top: -15px;
}
#output {
width: auto;
height: 100%;
background: black;
-webkit-transform: scale(-1, 1);
}
</style>
</head>
<body>
<p id="info">Please allow access to your camera!</p>
<canvas id="output"></canvas>
<script src="ccv.js"></script>
<script src="face.js"></script>
<script>
// requestAnimationFrame shim
(function() {
var i = 0,
lastTime = 0,
vendors = ['ms', 'moz', 'webkit', 'o'];
while (i < vendors.length && !window.requestAnimationFrame) {
window.requestAnimationFrame = window[vendors[i] + 'RequestAnimationFrame'];
i++;
}
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime(),
timeToCall = Math.max(0, 1000 / 60 - currTime + lastTime),
id = setTimeout(function() { callback(currTime + timeToCall); }, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
}());
var App = {
start: function(stream) {
App.video.addEventListener('canplay', function() {
App.video.removeEventListener('canplay');
setTimeout(function() {
App.video.play();
App.canvas.style.display = 'inline';
App.info.style.display = 'none';
App.canvas.width = App.video.videoWidth;
App.canvas.height = App.video.videoHeight;
App.backCanvas.width = App.video.videoWidth / 4;
App.backCanvas.height = App.video.videoHeight / 4;
App.backContext = App.backCanvas.getContext('2d');
var w = 300 / 4 * 0.8,
h = 270 / 4 * 0.8;
App.comp = [{
x: (App.video.videoWidth / 4 - w) / 2,
y: (App.video.videoHeight / 4 - h) / 2,
width: w,
height: h,
}];
App.drawToCanvas();
}, 500);
}, true);
var domURL = window.URL || window.webkitURL;
App.video.src = domURL ? domURL.createObjectURL(stream) : stream;
},
denied: function() {
App.info.innerHTML = 'Camera access denied!<br>Please reload and try again.';
},
error: function(e) {
if (e) {
console.error(e);
}
App.info.innerHTML = 'Please go to about:flags in Google Chrome and enable the "MediaStream" flag.';
},
drawToCanvas: function() {
requestAnimationFrame(App.drawToCanvas);
var video = App.video,
ctx = App.context,
backCtx = App.backContext,
m = 4,
w = 4,
i,
comp;
ctx.drawImage(video, 0, 0, App.canvas.width, App.canvas.height);
backCtx.drawImage(video, 0, 0, App.backCanvas.width, App.backCanvas.height);
comp = ccv.detect_objects(App.ccv = App.ccv || {
canvas: App.backCanvas,
cascade: cascade,
interval: 4,
min_neighbors: 1
});
if (comp.length) {
App.comp = comp;
}
for (i = App.comp.length; i--; ) {
ctx.strokeRect(App.glasses, (App.comp[i].x - w / 2) * m, (App.comp[i].y - w / 2) * m, (App.comp[i].width + w) * m, (App.comp[i].height + w) * m);
}
}
};
App.glasses = new Image();
App.glasses.src = 'glasses.png';
App.init = function() {
App.video = document.createElement('video');
App.backCanvas = document.createElement('canvas');
App.canvas = document.querySelector('#output');
App.canvas.style.display = 'none';
App.context = App.canvas.getContext('2d');
App.info = document.querySelector('#info');
navigator.getUserMedia_ = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
try {
navigator.getUserMedia_({
video: true,
audio: false
}, App.start, App.denied);
} catch (e) {
try {
navigator.getUserMedia_('video', App.start, App.denied);
} catch (e) {
App.error(e);
}
}
App.video.loop = App.video.muted = true;
App.video.load();
};
App.init();
</script>
</body>
</html>
</pre>
Any ideas?
var x_offset = 0, y_offset = 0, x_scale = 1, y_scale = 1;
if (App.video.Width * App.video.Height > localVideo.videoWidth * localVideo.clientHeight) {
x_offset = (localVideo.clientWidth - localVideo.clientHeight * localVideo.videoWidth / localVideo.videoHeight) / 2;
} else {
y_offset = (localVideo.clientHeight - localVideo.clientWidth * localVideo.videoHeight / localVideo.videoWidth) / 2;
}
x_scale = (localVideo.clientWidth - x_offset * 2) / localVideo.videoWidth;
y_scale = (localVideo.clientHeight - y_offset * 2) / localVideo.videoHeight;
for (var i = 0; i < comp.length; i++) {
comp[i].x = comp[i].x * x_scale + x_offset;
comp[i].y = comp[i].y * y_scale + y_offset;
comp[i].width = comp[i].width * x_scale;
comp[i].height = comp[i].height * y_scale;
and then use the line
ctx.strokeRect(comp[i].x, comp[i].y, comp[i].width, comp[i].height);
replace all localvideo.clientwidth by App.video.width and ocalvideo.clientheight by App.video.height