I have this snippet which puts a full-page <canvas> element into a 45-degree perspective using CSS. However, the transformed canvas exceeds the page's bounds and is clipped:
window.onload = window.onresize = function() {
// draw a gradient on the canvas
var canvas = document.querySelector('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext('2d');
var grad = ctx.createLinearGradient(0, 0, 0, canvas.height);
grad.addColorStop(0, '#ff0000');
grad.addColorStop(0.5, '#ffff00');
grad.addColorStop(1, '#00ff00');
ctx.fillStyle = grad;
ctx.fillRect(0, 0, canvas.width, canvas.height);
};
body {
margin: 0;
overflow: hidden;
}
canvas {
transform-style: preserve-3d;
transform: rotateX(45deg);
}
.container {
width: 100%;
height: 100%;
perspective: 50vw;
perspective-origin: 50% 0%;
}
<div class="container">
<canvas></canvas>
</div>
How would I ensure the entire canvas stays within the page's bounds (bottom side of the trapezoid should have the exact same width as the page), while maintaining the perspective effect? Either CSS or JavaScript solutions are fine, as long as they work with any page size.
translate it backwards using translateZ
look for a comment in CSS
window.onload = window.onresize = function() {
// draw a gradient on the canvas
var canvas = document.querySelector('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext('2d');
var grad = ctx.createLinearGradient(0, 0, 0, canvas.height);
grad.addColorStop(0, '#ff0000');
grad.addColorStop(0.5, '#ffff00');
grad.addColorStop(1, '#00ff00');
ctx.fillStyle = grad;
ctx.fillRect(0, 0, canvas.width, canvas.height);
};
body {
margin: 0;
overflow: hidden;
}
canvas {
transform-style: preserve-3d;
/* added translation here */
transform: translateZ(-50vmax) rotateX(45deg);
}
.container {
width: 100%;
height: 100%;
perspective: 50vw;
perspective-origin: 50% 0%;
}
<div class="container">
<canvas></canvas>
</div>
Related
So I want to recreate this simple animation effect, which uses a <canavs> :
https://codepen.io/w3devcampus/pen/PpLLKY
HTML:
<html lang="en">
<head>
<meta charset="utf-8">
<title>Draw a monster in a canvas</title>
</head>
<body>
<canvas id="myCanvas" width="200" height="200"></canvas>
</body>
</html>
JavaScript:
function mainLoop() {
ctx.clearRect(0, 0, w, h);
drawMyMonster(xMonster, yMonster);
xMonster += monsterSpeed;
if (((xMonster + 100)> w) || (xMonster < 0)) {
monsterSpeed = -monsterSpeed;
}
}
function drawMyMonster(x, y) {
ctx.save();
ctx.translate(x, y);
ctx.strokeRect(0, 0, 100, 100);
ctx.fillRect(20, 20, 10, 10);
ctx.fillRect(65, 20, 10, 10);
ctx.strokeRect(45, 40, 10, 40);
ctx.strokeRect(35, 84, 30, 10);
ctx.fillRect(38, 84, 10, 10);
ctx.fillRect(52, 84, 10, 10);
ctx.restore();
}
So, I did recreate it, but it wouldn't behave normally. Instead of displaying one frame at a time, it stacks all the frames up each other in the canvas. Here's my version:
https://codepen.io/Undefined_Variable/pen/LraLJQ
HTML:
<html>
<head>
<title>CanvasThingy</title>
</head>
<body>
<canvas id="CanvasForMonster">
No support for you scrub!
</canvas>
</body>
</html>
JavaScript:
var myCanvas, context, w, h;
var xMonster = 60;
var yMonster = 30;
var monsterSpeed = 1;
window.onload = function initial(){
myCanvas = document.body.querySelector('#CanvasForMonster');
w = myCanvas.style.width;
h = myCanvas.style.height;
context = myCanvas.getContext('2d');
ClearCanvas();
}
function ClearCanvas(){
context.clearRect(0, 0, w, h);
DrawFMLface(xMonster, yMonster);
xMonster += monsterSpeed;
if(((xMonster + 175) > 299) || (xMonster < 0) ){
monsterSpeed = -monsterSpeed;
}
requestAnimationFrame(ClearCanvas);
}
function DrawFMLface(x, y){
context.save();
context.translate(x, y);
context.strokeRect(0, 4, 175, 80);
context.strokeStyle = 'black';
context.fillRect(40, 10, 20, 20);
context.strokeStyle = 'black';
context.fillRect(110, 10, 20, 20);
context.strokeStyle = 'black';
context.strokeRect(85, 40, 1, 15);
context.strokeStyle = 'black';
context.strokeRect(42, 70, 90, 0);
context.strokeStyle = 'black';
context.restore();
}
Please tell me how to do this properly without jQuery AT ALL, please. I'm still learning and I don't want any jQuery/Angular/React etc. in my code, please!
Your problem is that the previous faces are not being cleared. The clearing is supposed to be done with this line:
ctx.clearRect(0, 0, w, h);
The problem is that w and h are not set correctly when this is run, so not enough of the canvas is cleared.
This is where you set w and h:
w = myCanvas.style.width;
h = myCanvas.style.height;
This will only work if the width and height were set with CSS, e.g. with an HTML attribute style="width: 200px; height: 200px;", and if you called parseInt to convert the CSS style strings into numbers. But in fact the canvas element’s width and height were set with width and height HTML attributes directly. So you should set w and h with HTMLCanvasElement’s width and height properties:
w = canvas.width;
h = canvas.height;
The original Codepen sets them this way.
Just in case you were looking for a possibly easier way to do this, you can actually avoid JavaScript entirely when creating a simple animation like this by using CSS animations:
#CanvasForMonster {
position: relative;
width: 200px;
height: 200px;
border: 1px solid #000;
}
#Monster {
position: absolute;
top: 25%;
width: 50%;
height: 50%;
animation: bounce 2s alternate linear infinite;
}
#keyframes bounce {
from { left: 0; }
to { left: 50%; }
}
#Monster div {
position: absolute;
}
#Monster .stroke {
border: 1px solid #000;
}
#Monster .fill {
background-color: #000;
}
#Monster .head {
left: 0;
top: 0;
width: 100%;
height: 100%;
}
#Monster .eye {
width: 10%;
height: 10%;
}
#Monster .eye-left {
left: 25%;
top: 25%;
}
#Monster .eye-right {
left: 65%;
top: 25%;
}
#Monster .nose {
left: 50%;
top: 50%;
width: 1px;
height: 10%;
}
#Monster .mouth {
left: 25%;
top: 75%;
width: 50%;
height: 0;
}
<div id="CanvasForMonster">
<div id="Monster">
<div class="stroke head"></div>
<div class="fill eye eye-left"></div>
<div class="fill eye eye-right"></div>
<div class="stroke nose"></div>
<div class="stroke mouth"></div>
</div>
</div>
Everything you have is alright.
The problem is your Globals.
The width of the canvas should be set to:
w = myCanvas.width;
Height set to:
h = myCanvas.height;
You have both set as:
w = myCanvas.style.width;
h = myCanvas.style.height;
Which will not work.
Here is the fork (Demo).
I would highly recommend using JQuery when working with canvas.
When using canvas it should be noted that you want too reference canvas.height, and canvas.width as much as possible rather then using a global that points at the height, and width of the css style.
i am trying to get the rgb color data from a painted canvas.
with this function:
function pick(event) {
var x = event.layerX;
var y = event.layerY;
var pixel = ctx.getImageData(x, y, 1, 1);
var data = pixel.data;
$("#color").val(pixel.data.toString());
console.log(pixel.data.toString());
}
however, when i am setting the canvas element to any relative width (80%/100%), like this:
canvas {
/*width: 100%;*/ /*when i set the width, the rgb values are incorrect .... */
/*margin-left: auto;
margin-right: auto;
display: block;*/
}
i am no longer getting correct values from this function.
attaching my JS Fiddle:
https://jsfiddle.net/iliran11/ay9nf1p9/
You need to find the difference in width when you stretch and multiply that to the coordinates.
//color names
var basecolors = ["(255,0,0)",
"(255,128,0)",
"(255,255,0)",
"(128,255,0)",
"(0,255,0)",
"(0,255,128)",
"(0,255,255)",
"(0,128,255)",
"(0,0,255)",
"(128,0,255)",
"(255,0,255)",
"(255,0,128)",
"(128,128,128)"
];
//init
var ctx = canvas.getContext("2d");
var width = canvas.width;
var height = canvas.height;
var heightPerColor = height / basecolors.length;
//init cursor
var xCursor = 0;
var yCursor = 0;
drawPallete(width, height, "s");
function drawPallete(width, height, type) {
canvas.width = width;
canvas.height = height;
ctx.clearRect(0, 0, canvas.width, canvas.height);
var Grad = ctx.createLinearGradient(0, 0, canvas.width, 0);
Grad.addColorStop(0 / 6, '#F00');
Grad.addColorStop(1 / 6, '#FF0');
Grad.addColorStop(2 / 6, '#0F0');
Grad.addColorStop(3 / 6, '#0FF');
Grad.addColorStop(4 / 6, '#00F');
Grad.addColorStop(5 / 6, '#F0F');
Grad.addColorStop(6 / 6, '#F00');
ctx.fillStyle = Grad;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
;
function pick(event) {
var canvas = document.getElementById("canvas");
//Here comes the stretch offset
//var off = (1 / canvas.width) * canvas.offsetWidth;
var off = (1 / canvas.offsetWidth) * canvas.width;
//Applying offset
var x = Math.floor(event.layerX * off) - Math.floor(canvas.offsetLeft * off);
var y = Math.floor(event.layerY * off) - Math.floor(canvas.offsetTop * off);
var pixel = ctx.getImageData(x, y, 1, 1);
var data = pixel.data;
$("#color").val([pixel.data[0], pixel.data[1], pixel.data[2], pixel.data[3]].join(','));
//console.log("offset between", canvas.width, "and", canvas.offsetWidth, "is", off);
console.log(off, canvas.offsetWidth, canvas.width, x);
}
// target the button
var btn = $("#myBtn");
var modal = $("#myModal");
btn.click(function () {
modal.css("display", "block");
var canvas = document.getElementById("canvas");
canvas.addEventListener('mousemove', pick);
});
#myModal {
display: none;
/* Hidden by default */
position: fixed;
/* Stay in place */
z-index: 1;
/* Sit on top */
padding-top: 100px;
/* Location of the box */
left: 0;
top: 0;
width: 100%;
/* Full width */
height: 100%;
/* Full height */
overflow: auto;
/* Enable scroll if needed */
background-color: rgb(0, 0, 0);
/* Fallback color */
background-color: rgba(0, 0, 0, 0.4);
}
.modal-content {
background-color: #fefefe;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
}
canvas {
width: 100%;
/*margin-left: auto;
margin-right: auto;
display: block;*/
}
<form action="" method="post">
<label for="POST-name">Name:</label>
<input id="color" type="text">
</form>
<button id="myBtn">Open Color</button>
<div id="myModal" class="modal">
<!-- Modal content -->
<div class="modal-content">
<!-- Set height and width -->
<canvas id="canvas" width="1000" height="1000"></canvas>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.1.1.js" integrity="sha256-16cdPddA6VdVInumRGo6IbivbERE8p7CQR3HzTBuELA=" crossorigin="anonymous"></script>
EDIT 1
Compensated for offsetLeft and offsetTop.
An HTML canvas element can be automatically resized by the dynamics of a HTML page: Maybe because its style properties are set to a percentage value, or maybe because it is inside a flex container.
Thereby the content of the canvas gets resized as well: Because of the interpolation the canvas content looks blurred (loss of resolution).
In order to keep the resolution to the maximum, one must render the canvas content based on the current pixel size of the element.
This fiddle shows how this can be done with the help of the function getComputedStyle: https://jsfiddle.net/fx98gmu2/1/
setInterval(function() {
var computed = getComputedStyle(canvas);
var w = parseInt(computed.getPropertyValue("width"), 10);
var h = parseInt(computed.getPropertyValue("height"), 10);
canvas.width = w;
canvas.height = h;
// re-rendering of canvas content...
}, 2000); // intentionally long to see the effect
With a setTimeout function I'm updating the width and height of the canvas to the computed values of these properties.
As the fiddle shows, this works only when increasing the size of the window. Whenever the window size gets decreased, the canvas (item of the flexbox) stays fixed.
Set its flex-basis to 0, allow it to shrink with flex-shrink, and don't force any minimum width:
#canvas {
flex: 1 1 0;
min-width: 0;
}
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
setInterval(function() {
var computed = getComputedStyle(canvas),
w = parseInt(computed.width, 10),
h = parseInt(computed.height, 10);
canvas.width = w;
canvas.height = h;
ctx.clearRect(0, 0, w, h);
ctx.beginPath();
ctx.arc(w/2, h/2, h/2, 0, Math.PI*2, true);
ctx.stroke();
}, 2000); // intentionally long to see the effect
#container {
border: 1px solid blue;
width: 100%;
display: flex;
}
#canvas {
border: 1px solid red;
flex: 1 1 0;
min-width: 0;
height: 150px;
}
<div id="container">
<canvas id="canvas"></canvas>
<div>something else...</div>
</div>
You need to place the canvas into a container with overflow:hidden and set canvas dimensions to dimensions of that container:
window.setTimeout( function() {
var div = $("div");
div.find("canvas").attr( { width:div.width(), height:div.height()} );
})
canvas { background:gold; display:block; }
div { width:50%; height:50%; border:1px solid; overflow:hidden; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<canvas width="200" height="100" />
</div>
I'm trying to draw into a canvas, but the more I go right, the more offset my drawing become.
Anyone have an idea why?
I have included the relevant code below:
CSS
html,body {
width:100%;
height:100%;
background:rgba(0,0,0,.2);
}
#container {
position:relative;
width:700px;
height:450px;
background:#fff;
overflow:hidden;
}
* {
-webkit-user-select: none;
}
canvas {
position: absolute;
top: 0;
left: 0;
background: #ccc;
width: 500px;
height: 200px;
}
HTML
<div id='adContainer'>
<canvas></canvas>
</div>
Javascript
var ctx;
var can = $('canvas');
$(document).ready(function() {
ctx = can[0].getContext('2d');
ctx.strokeStyle = "rgba(255,0,0,1)";
ctx.lineWidth = 5;
ctx.lineCap = 'round';
can.on("touchstart", function(event) {
event.preventDefault();
var e = event.originalEvent;
if(e.touches.length == 1) {
var posX = e.touches[0].pageX;
var posY = e.touches[0].pageY;
ctx.moveTo(posX, posY);
}
});
can.on("touchmove", function(event) {
event.preventDefault();
var e = event.originalEvent;
if(e.touches.length == 1) {
var posX = e.touches[0].pageX;
var posY = e.touches[0].pageY;
ctx.lineTo(posX, posY);
ctx.stroke();
}
});
});
Demo: http://jsfiddle.net/8Wtf8/
That is because you define the size of the canvas using CSS.
What happens is that when you don't explicitly define the size of the canvas using its width and height attributes the canvas defaults to size 300 x 150.
In your CSS you are then stretching the canvas element (look at it as an image) to 500px etc. - the content of the canvas is still 300 x 150.
You need to set the width and height on the canvas element itself:
<canvas width=500 height=200 id="myCanvas"></canvas>
and remove the definition from the CSS:
canvas {
position: absolute;
top: 0;
left: 0;
background: #ccc;
/*width: 500px;
height: 200px;*/
}
Also notice that background set by CSS will not be part of the canvas content.
In Java script I am changing the height and width of the HTML5 canvas. It works in Mozila, Chrome, opera and safari, But it is not working in IE9. Can any body tell me what I am doing wrong, do I need to set any property?
Thanks in advance
UPDATE:-
HTML:-
<body onload="Load();">
<canvas id="myCanvas" width="500" height="300"></canvas>
</body>
Javascript:-
function Load(){
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext('2d');
img=new Image();
img.src= "data:image/bmp;base64,"+respObj.respData; //Adding bmp image header
img.onload = function(){
canvas.width = 300;
canvas.height = 500;
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(img,0,0,canvas.width, canvas.height);
}
}
CSS:-
.canvas{
height: auto;
width: auto;
margin : 0px 35px;
border:1px solid black;
position: relative;
}
Try the following code. It is working in IE9, Firefox, Chrome :
<!DOCTYPE html>
<html>
<style language="css/text">
.canvas{
height: auto;
width: auto;
margin : 0px 35px;
border:1px solid black;
position: relative;
}
</style>
<body onload="Load();">
<canvas id="myCanvas" width="500" height="300"></canvas>
<input type="button" onClick="change()"/>
</body>
<script>
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext('2d');
function Load(){
img=new Image();
//img.src= "data:image/bmp;base64,"+respObj.respData; //Adding bmp image header
canvas.width = 300;
canvas.height = 500;
context.fillStyle="#FF0000";
context.fillRect(0, 0, canvas.width, canvas.height);
//context.drawImage(img,0,0,canvas.width, canvas.height);
}
function change() {
canvas.width = 100;
canvas.height = 100;
context.fillStyle="#FF0000";
context.fillRect(0, 0, canvas.width, canvas.height);
}
</script>
</html>
I found the issue with my canvas. It was the issue with style. I have removed the height and width setting from the style script. Because of it, the canvas height and width was overriding to its default values i.e. 150 and 300 as height and width respectively.