How not to make the canvas flash when i call clearRect [duplicate] - javascript

I've a screen being redraw every 25ms, and images are flickering, here is my code
var FRAME_RATE = 40;
var intervalTime = 1000/FRAME_RATE;
gameLoop();
function gameLoop(){
context.clearRect(0, 0, 640, 640);
renderMap();
window.setTimeout(gameLoop, intervalTime);
}
and here is renderMap() function
function renderMap(){
var startX = playerX - (screenW / 2);
var startY = playerY - (screenH / 2);
maxX = playerX + (screenW / 2);
maxY = playerY + (screenH / 2);
$.getJSON('mapa3.json', function(json){
for (x = startX; x < maxX; x=x+32){
for (y = startY; y < maxY; y=y+32){
intTile = json.layers[0].data[((y/32)* 100) + (x/32)];
context.putImageData(getTile(intTile - 1), x - startX, y - startY);
}
}
});
var imgCharacter = new Image();
imgCharacter.src = 'char.png';
var posX = (screenW - imgCharacter.width) / 2;
var posY = (screenH - imgCharacter.height) / 2;
imgCharacter.onload = function(){context.drawImage(imgCharacter, posX, posY)}
}
What changes do I need to make to the code to stop flickering?

I believe it is because you are loading the image each iteration. Try putting the var imgCharacter..., the following line, and the image's onload function outside of renderMap so it is only ran once
var imgCharacter = new Image();
imgCharacter.onload = function () {
renderMap();
}
imgCharacter.src = 'char.png';
function renderMap() {
var startX = playerX - (screenW / 2);
var startY = playerY - (screenH / 2);
maxX = playerX + (screenW / 2);
maxY = playerY + (screenH / 2);
$.getJSON('mapa3.json', function (json) {
for (x = startX; x < maxX; x = x + 32) {
for (y = startY; y < maxY; y = y + 32) {
intTile = json.layers[0].data[((y / 32) * 100) + (x / 32)];
context.putImageData(getTile(intTile - 1), x - startX, y - startY);
}
}
});
var posX = (screenW - imgCharacter.width) / 2;
var posY = (screenH - imgCharacter.height) / 2;
context.drawImage(imgCharacter, posX, posY)
}
Thank you to markE for letting me know the onload function also needs to go outside renderMap, I overlooked it the first time

Load all images and other data before draw and storage them inside array.
Better use requestAnimationFrame.
Remember, getting JSON (or other data) can take some time.

Related

In round range slider, how can i change background color of the changed path?

I have got this function but i am unable to get the path to which the slider when dragged should change the background color of the circle...
$(document).ready(function () {
$("#circle").append('<canvas id="slideColorTracker" class="slideColor"></canvas>');
var canvas = document.getElementById("slideColorTracker");
var context = canvas.getContext('2d');
var $circle = $("#circle"),
$handler = $("#handler"),
$p = $("#test").val(),
handlerW2 = $handler.width() / 2,
radius = ($circle.width()/2)+10,
offset = $circle.offset(),
elementPos = { x: offset.left, y: offset.top },
mHold = 0,
PI = Math.PI / 180
$handler.mousedown(function () {
mHold = 1;
var dim = $("#slideColorTracker");
if ($(".slideColor").length > 1) {
$("#slideColorTracker").remove();
}
});
$(document).mousemove(function (e){
move(e);
}).mouseup(function () { mHold = 0 });
function move(e) {
if (mHold) {
debugger
var deg = 180;
var startAngle = 4.72;
var mousePos = { x: e.pageX - elementPos.x, y: e.pageY - elementPos.y }
var atan = Math.atan2(mousePos.x - radius, mousePos.y - radius);
var deg = -atan / PI + 180;
var percentage = (deg * 100 / 360) | 0;
var endAngle = percentage;
var X = Math.round(radius * Math.sin(deg * PI)),
Y = Math.round(radius * -Math.cos(deg * PI));
var cw = X + radius - handlerW2 - 10; //context.canvas.width /2;
var ch = Y + radius - handlerW2 - 10;
$handler.css({
left: X + radius - handlerW2 - 10,
top: Y + radius - handlerW2 - 10,
})
context.beginPath();
context.arc(($circle.width() / 2), ($circle.height() / 2), radius+20, startAngle, endAngle, false);
context.lineWidth = 15;
context.strokeStyle = 'black';
context.stroke();
$("#test").val(percentage);
$circle.css({ borderColor: "hsl(200,70%," + (percentage * 70 / 100 + 30) + "%)" });
}
}
});
I do not want to use any plugin!
Any help will be much appreciated, since i am new to jquery!
Your $handler does not drag for me unless I change it to this:
$handler.css({
left: mousePos.x,
top: mousePos.y,
position:'absolute'
})
When dragged passed $cirlce.width / 2 it does stroke it correctly.

Why isn't my program drawing anything?

I made this program that will hopefully draw a cool shape based on the sliders. The problem is, nothing is showing up other than the sliders! Why is this?
var aslider, lslider, sslider, newx, newy;
function setup() {
createCanvas(300,300);
aslider = createSlider(1,359,17);
lslider = createSlider(1,50,10);
sslider = createSlider(1,50,20);
}
function draw() {
var angle = aslider.value();
var length = lslider.value();
var size = sslider.value();
var startx = width / 2;
var starty = height / 2;
var radians = angle * (Math.PI/180);
for (var i = 0; i++; i < size) {
newx = startx * Math.sin(radians) + starty * Math.cos(radians);
newy = startx * Math.sin(radians) + starty * -Math.cos(radians);
line(startx, starty, newx, newy);
startx = newx;
starty = newy;
}
}
Your for loop is wrong. It should be
for (var i = 0; i < size; i++)
I tested it here: http://codepen.io/eerk/pen/wgOZVW?editors=1010

dragging scaled image placed in canvas

I have an issue with dragging image that is scaled.
I've created main_canvas that contains background and image represented via bg_canvas resp. img_canvas. Then I've made coordinates of background (panoramaX, panoramaY) and image(imageX, imageY) to check if I clicked some of them and to be able to drag them.
Image is placed on top of background:
var drawToMain = function()
{
// first clear the canvas
main_ctx.clearRect(0,0,canvas.width, canvas.height);
ctx.scale(scaleFactor, scaleFactor);
// draw the background image
main_ctx.drawImage(bg_canvas, panoramaX,panoramaY, 600, 300);
// do the transforms
main_ctx.translate( imageX+img_canvas.width/2,
imageY+img_canvas.height/2);
main_ctx.rotate(angle);
main_ctx.translate( -(imageX+img_canvas.width/2),
-(imageY+img_canvas.height/2));
// draw the img with the transforms applied
main_ctx.drawImage(img_canvas, imageX, imageY);
// reset the transforms
main_ctx.setTransform(1,0,0,1,0,0);
};
To drag items I'm testing if I clicked one of them:
function hitImage(x, y) {
if (x > imageX_hit && x < imageX_hit + img_canvas.width * scaleFactor &&
y > imageY_hit && y < imageY_hit + img_canvas.height * scaleFactor)
return 1;
if (x > panoramaX && x < panoramaX + bg_canvas.width * scaleFactor &&
y > panoramaY && y < panoramaY + bg_canvas.height * scaleFactor)
return 2;
return 0;
}
Then I've made wheel event to scale content of main_canvas (background and image, both) where is my issue, I'm updating image coordinates there but it doesn't work for me, when I zoom-out image for example and try to drag it, it doesn't move and when I'm zoomig image is "travelling" in background:
canvas.addEventListener("wheel", myFunction);
canvas.addEventListener('DOMMouseScroll', myFunction);
function myFunction(evt) {
var delta = evt.wheelDelta ? evt.wheelDelta / 40 : evt.detail ? -evt.detail : 0;
if (delta > 0) scaleFactor = scaleFactor + 0.05;
else scaleFactor = scaleFactor - 0.05;
imageX = imageX * scaleFactor;
imageY = imageY * scaleFactor;
imageX_hit = imageX_hit * scaleFactor;
imageY_hit = imageY_hit * scaleFactor;
drawToMain();
}
THERE is a exapmle of my problem.
Is there any solution, please?
Here is the code (fixed Slavik's code, update 1):
$(document).ready(function() {
// x, y to place image
var imageX = 80,
imageY = 80;
var imageX_hit = 80,
imageY_hit = 80;
// x, y to place background image
var panoramaX = 0,
panoramaY = 0;
var startX,
startY,
dragImage;
// mouse coordinates in canvas
var mouseX,
mouseY;
// number represents scale of image and background
var scaleFactor = 1,
angle = 0;
// main canvas
var canvas = document.createElement('canvas');
canvas.width = 600;
canvas.height = 300;
document.body.appendChild(canvas);
var ctx = canvas.getContext('2d');
var canvasOffset = $(canvas).offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var main_ctx = canvas.getContext('2d');
var bg_canvas = canvas.cloneNode(); // canvas for image
var img_canvas = canvas.cloneNode();// canvas for background
var angle = 0;
// draw on the main canvas, and only on the main canvas
var drawToMain = function()
{
// first clear the canvas
main_ctx.clearRect(0,0,canvas.width, canvas.height);
ctx.scale(scaleFactor, scaleFactor);
// draw the background image
main_ctx.drawImage(bg_canvas, panoramaX,panoramaY, 600, 300);
// do the transforms
main_ctx.translate( imageX+img_canvas.width/2,
imageY+img_canvas.height/2);
main_ctx.rotate(angle);
main_ctx.translate( -(imageX+img_canvas.width/2),
-(imageY+img_canvas.height/2));
// draw the img with the transforms applied
main_ctx.drawImage(img_canvas, imageX, imageY);
// reset the transforms
main_ctx.setTransform(1,0,0,1,0,0);
};
// I changed the event to a simple onclick
$(canvas).mousedown(function(e) {
switch (e.which) {
case 1:
// mouse position and test if image is hit
startX = parseInt(e.clientX - offsetX);
startY = parseInt(e.clientY - offsetY);
dragImage = hitImage(startX, startY);
break;
default:
break;
}
});
$(canvas).mousemove(function(e) {
// Im moving with image
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// move image by the amount of the latest drag
var dx = (mouseX - startX)/scaleFactor;
var dy = (mouseY - startY)/scaleFactor;
if (dragImage == 1) {
imageX += dx;
imageY += dy;
imageX_hit += dx;
imageY_hit += dy;
// reset the startXY for next time
startX = mouseX;
startY = mouseY;
drawToMain();
}
else if (dragImage == 2) {
panoramaX += dx;
panoramaY += dy;
imageX += dx;
imageY += dy;
imageX_hit += dx;
imageY_hit += dy;
// reset the startXY for next time
startX = mouseX;
startY = mouseY;
drawToMain();
}
});
$(canvas).mouseup(function(e) {
switch (e.which) {
case 1:
dragImage = 0;
break;
default:
break;
}
});
// ZOOM-IN or ZOOM-OUT
canvas.addEventListener("wheel", myFunction);
canvas.addEventListener('DOMMouseScroll', myFunction);
// COMPUTING scaleFactor - zoom
function myFunction(evt) {
var delta = evt.wheelDelta ? evt.wheelDelta / 40 : evt.detail ? -evt.detail : 0;
if (delta > 0) scaleFactor = scaleFactor + 0.05;
else scaleFactor = scaleFactor - 0.05;
//imageX = imageX * scaleFactor;
//imageY = imageY * scaleFactor;
//console.log(imageX, imageY);
//imageX_hit = imageX_hit * scaleFactor;
//imageY_hit = imageY_hit * scaleFactor;
drawToMain();
}
// test if I user clicked image or panorama
function hitImage(x, y) {
x=x/scaleFactor;
y=y/scaleFactor;
if (x > imageX_hit && x < imageX_hit + img_canvas.width &&
y > imageY_hit && y < imageY_hit + img_canvas.height)
return 1;
if (x > panoramaX && x < panoramaX + bg_canvas.width &&
y > panoramaY && y < panoramaY + bg_canvas.height)
return 2;
return 0;
}
// loading images into canvas
var img = new Image();
img.onload = function() {
var this_canvas = img_canvas;
this_canvas.width = 150;
this_canvas.height = 150;
this_canvas.getContext('2d').drawImage(this, 0,
0, this_canvas.width,
this_canvas.height);
drawToMain();
};
img.src = 'http://pgmagick.readthedocs.org/en/latest/_images/lena_scale.jpg';
var bg = new Image();
bg.onload = function() {
var this_canvas = bg_canvas;
this_canvas.getContext('2d').drawImage(this, panoramaX,
panoramaY, 600, 300);
drawToMain();
};
bg.src = 'http://www.ansteorra.org/graphics/background/bg_image.jpg';
});
You don't need to change imageX, imageY and imageX_hit, imageY_hit variables by multiplying on scaleFactor. You scale image by context.scale so you don't need change something in your coordinates. Just remember scale factor and use it in hitImage for checking coordinates.
I'll try to edit post and explain more clearly, if this would be solution for your problem:
https://jsfiddle.net/j1f306w5/1/
My changes:
function hitImage(x, y) {
if (x > imageX_hit * scaleFactor && x < imageX_hit * scaleFactor + img_canvas.width * scaleFactor &&
y > imageY_hit * scaleFactor && y < imageY_hit * scaleFactor + img_canvas.height * scaleFactor)
return 1;
if (x > panoramaX * scaleFactor && x < panoramaX * scaleFactor + bg_canvas.width * scaleFactor &&
y > panoramaY * scaleFactor && y < panoramaY * scaleFactor + bg_canvas.height * scaleFactor)
return 2;
return 0;
}
and
function myFunction(evt) {
var delta = evt.wheelDelta ? evt.wheelDelta / 40 : evt.detail ? -evt.detail : 0;
if (delta > 0) scaleFactor = scaleFactor + 0.05;
else scaleFactor = scaleFactor - 0.05;
//imageX = imageX * scaleFactor;
//imageY = imageY * scaleFactor;
//imageX_hit = imageX_hit * scaleFactor;
//imageY_hit = imageY_hit * scaleFactor;
drawToMain();
}

Rectangle Coordinate Projection into another Rectangle

So I would like to project coordinates from one rectangle in javascript to another, in essence, scaling coordinates from one rectangle to another. How would I do that?
function Rectangle(maxX, minX, maxY, minY) {
this.maxX = maxX;
this.minX = minX;
this.maxY = maxY;
this.minY = minY;
}
So 2 Rectangles with different size and coordinates. I want to draw into one rectangle and scale that proportionally into the other rectangle.
Something like this?
var src = new Rectangle(6, 0, 6, 2);
var dst = new Rectangle(4, 2, 8, 0);
function Rectangle(maxX, minX, maxY, minY) {
this.maxX = maxX;
this.minX = minX;
this.maxY = maxY;
this.minY = minY;
}
var canvas = document.querySelector("canvas");
var width = canvas.width, height = canvas.height;
var w = width / 2 + 0.5, h = height / 2 + 0.5;
var sx = width / 20, sy = height / 20;
var context = canvas.getContext("2d");
context.strokeStyle = "#808080";
context.fillStyle = "#000080";
tween(src, dst, 25, 1, 1000);
function tween(src, dst, fps, seconds, delay) {
var frames = fps * seconds;
var srcMaxX = src.maxX;
var srcMinX = src.minX;
var srcMaxY = src.maxY;
var srcMinY = src.minY;
var maxX = dst.maxX - srcMaxX;
var minX = dst.minX - srcMinX;
var maxY = dst.maxY - srcMaxY;
var minY = dst.minY - srcMinY;
drawRectangle(srcMaxX, srcMinX, srcMaxY, srcMinY);
setTimeout(function loop(frame, delay) {
var dstMaxX = srcMaxX + maxX * frame / frames;
var dstMinX = srcMinX + minX * frame / frames;
var dstMaxY = srcMaxY + maxY * frame / frames;
var dstMinY = srcMinY + minY * frame / frames;
drawRectangle(dstMaxX, dstMinX, dstMaxY, dstMinY);
if (frame < frames) setTimeout(loop, delay, frame + 1, delay);
}, delay, 1, 1000 / fps);
}
function drawRectangle(maxX, minX, maxY, minY) {
context.clearRect(0, 0, width, height);
drawLine(0, h, width, h);
drawLine(w, 0, w, height);
var x = w + sx * minX;
var y = h - sy * maxY;
var wth = sx * (maxX - minX);
var hgt = sy * (maxY - minY);
context.fillRect(x, y, wth, hgt);
}
function drawLine(x1, y1, x2, y2) {
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.stroke();
}
canvas {
border: 1px solid #000000;
}
<canvas width="400" height="400"></canvas>
Hope this helps.
With these functions:
function Point(x, y) {
this.x = x;
this.y = y;
}
function Rect(xMin, xMax, yMin, yMax) {
this.xMin = xMin;
this.xMax = xMax;
this.yMin = yMin;
this.yMax = yMax;
}
Rect.prototype.width = function() {
return this.xMax - this.xMin;
};
Rect.prototype.height = function() {
return this.yMax - this.yMin;
};
function RectProjection(a, b) {
var xScale = b.width() / a.width();
var yScale = b.height() / a.height();
var xOffset = b.xMin - xScale * a.xMin;
var yOffset = b.yMin - yScale * a.yMin;
return function (p) {
return Point(xScale * p.x + xOffset, yScale * p.y + yOffset);
};
}
You can then do something like this:
var proj = projection(new Rectangle(0, 2, 0, 3), new Rectangle(2, 6, 2, 8));
var q = proj(new Point(1, 2));
Where q is Point(4, 6).

redrawing canvas html5 without flickering

I've a screen being redraw every 25ms, and images are flickering, here is my code
var FRAME_RATE = 40;
var intervalTime = 1000/FRAME_RATE;
gameLoop();
function gameLoop(){
context.clearRect(0, 0, 640, 640);
renderMap();
window.setTimeout(gameLoop, intervalTime);
}
and here is renderMap() function
function renderMap(){
var startX = playerX - (screenW / 2);
var startY = playerY - (screenH / 2);
maxX = playerX + (screenW / 2);
maxY = playerY + (screenH / 2);
$.getJSON('mapa3.json', function(json){
for (x = startX; x < maxX; x=x+32){
for (y = startY; y < maxY; y=y+32){
intTile = json.layers[0].data[((y/32)* 100) + (x/32)];
context.putImageData(getTile(intTile - 1), x - startX, y - startY);
}
}
});
var imgCharacter = new Image();
imgCharacter.src = 'char.png';
var posX = (screenW - imgCharacter.width) / 2;
var posY = (screenH - imgCharacter.height) / 2;
imgCharacter.onload = function(){context.drawImage(imgCharacter, posX, posY)}
}
What changes do I need to make to the code to stop flickering?
I believe it is because you are loading the image each iteration. Try putting the var imgCharacter..., the following line, and the image's onload function outside of renderMap so it is only ran once
var imgCharacter = new Image();
imgCharacter.onload = function () {
renderMap();
}
imgCharacter.src = 'char.png';
function renderMap() {
var startX = playerX - (screenW / 2);
var startY = playerY - (screenH / 2);
maxX = playerX + (screenW / 2);
maxY = playerY + (screenH / 2);
$.getJSON('mapa3.json', function (json) {
for (x = startX; x < maxX; x = x + 32) {
for (y = startY; y < maxY; y = y + 32) {
intTile = json.layers[0].data[((y / 32) * 100) + (x / 32)];
context.putImageData(getTile(intTile - 1), x - startX, y - startY);
}
}
});
var posX = (screenW - imgCharacter.width) / 2;
var posY = (screenH - imgCharacter.height) / 2;
context.drawImage(imgCharacter, posX, posY)
}
Thank you to markE for letting me know the onload function also needs to go outside renderMap, I overlooked it the first time
Load all images and other data before draw and storage them inside array.
Better use requestAnimationFrame.
Remember, getting JSON (or other data) can take some time.

Categories