Canvas - Keeping aspect ratio while also keeping pixel amount - javascript

I have a canvas like so:
But when resizing the window and redrawing, you can get anything in between.
I keep the aspect ratio with this code:
function setCanvasSize(){
ctx.canvas.width = (window.innerWidth)*0.8;
ctx.canvas.height= (window.innerWidth)*0.5;
}
setCanvasSize()
$(window).resize(function(){
setCanvasSize()
})
and that keeps it nice and clean, the same-looking size, but how many pixels are in there changes, so when my program draws, it's always in a different spot, although the aspect is the same, so it looks really weird.
Any way that I can somehow tweak how I draw things, or a way to keep how many pixels there are?
Edit: code snippet to reproduce
const ctx = document.getElementById('game').getContext("2d");
$('#game').css('width', '100%');
$(window).resize(function(){
$('#game').height($('#game').width() / 2.031);
});
const imageAt=((href,x,y,sizex,sizey)=>{
var img = new Image();
img.addEventListener('load', function() {
ctx.drawImage(img, x, y, sizex, sizey);
}, false);
img.src = href
})
imageAt('https://b.thumbs.redditmedia.com/bZedgr0gq7RQBBnVYVc-Nmzdr-5vEUg4Dj8nTrMb7yA.png',0,0,50,50)
#game{
padding-left: 0;
padding-right: 0;
display: block;
margin: 0;
border:5px solid green;
padding:0;
}
#vertical-center {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
background: black;
padding:0;
}
body{
background-color: black;
padding:0;
}
<html>
<head>
<title>Tiled Canvas Engine Test 1</title>
<meta name="viewport" content="width=device-width, initial-scale=0.75">
<link rel="stylesheet" href="./index.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="engine.js"></script>
<script defer src="script.js"></script>
</head>
<body>
<div id="vertical-center">
<canvas id="game"></canvas>
</div>
</body>
</html>

In order to render properly the canvas element needs to have its width and height attributes set explicitly. This will define the viewport into which graphics will be draw. The style attributes for width and height which is what jQuery sets via the width() and height() functions will only stretch the viewport to fit. (Note: for high DPI displays, i.e. Retina, you will want to double the dimensions when specifying the attributes).
The critical change is this:
// Updates the actual width and height attributes
canvas.width = $('#game').width() * window.devicePixelRatio;
canvas.height = $('#game').height() * window.devicePixelRatio;
The snippet below also handles clearing the canvas, redrawing the image, and saving the Image once loaded:
const ctx = document.getElementById('game').getContext("2d");
$('#game').css('width', '100%');
$(document).ready(handleResize);
$(window).resize(handleResize);
function handleResize() {
// sets the style.height and style.width for the canvas
$('#game').height($('#game').width() / 2.031);
let canvas = $('#game').get(0);
// Updates the actual width and height attributes
canvas.width = $('#game').width() * window.devicePixelRatio;
canvas.height = $('#game').height() * window.devicePixelRatio;
// redraw
if (img) {
redraw();
}
}
var img;
const imageAt = ((href, x, y, sizex, sizey) => {
img = new Image();
img.addEventListener('load', function() {
ctx.drawImage(img, x, y, sizex, sizey);
}, false);
img.src = href
})
imageAt('https://b.thumbs.redditmedia.com/bZedgr0gq7RQBBnVYVc-Nmzdr-5vEUg4Dj8nTrMb7yA.png', 0, 0, 50 * window.devicePixelRatio, 50 * window.devicePixelRatio)
function redraw() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(img, 0, 0, 50 * window.devicePixelRatio, 50 * window.devicePixelRatio);
}
#game {
padding-left: 0;
padding-right: 0;
display: block;
margin: 0;
border: 5px solid green;
padding: 0;
}
#vertical-center {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
background: black;
padding: 0;
}
body {
background-color: black;
padding: 0;
}
<html>
<head>
<title>Tiled Canvas Engine Test 1</title>
<meta name="viewport" content="width=device-width, initial-scale=0.75">
<link rel="stylesheet" href="./index.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="engine.js"></script>
<script defer src="script.js"></script>
</head>
<body>
<div id="vertical-center">
<canvas id="game"></canvas>
</div>
</body>
</html>

Related

Why is my cursor and my line of draw are in diffrent sides?

I am attempting to create a drawing app in JS, however, whenever anything is drawn, it is positioned away from my cursor depending on where it is on the canvas, when I am on the furthest left/bottom side of the canvas, you can draw where your cursor is, but the further right/up I move, the more the brush begins to "drift" and go further than where my cursor is.
const canvas = document.getElementById("canvas");
const increaseBtn = document.getElementById("increase");
const decreaseBtn = document.getElementById("decrease");
const sizeEl = document.getElementById("size");
const colorEl = document.getElementById("color");
const clearEl = document.getElementById("clear");
//Core Drawing Functionality (with some research)
const ctx = canvas.getContext("2d");
let size = 5;
let isPressed = false;
let color = "black";
let x;
let y;
let fakeSize = 1;
canvas.addEventListener("mousedown", (e) => {
isPressed = true;
x = e.offsetX;
y = e.offsetY;
});
canvas.addEventListener("mouseup", (e) => {
isPressed = false;
x = undefined;
y = undefined;
});
canvas.addEventListener("mousemove", (e) => {
if (isPressed) {
const x2 = e.offsetX;
const y2 = e.offsetY;
drawCircle(x2, y2);
drawLine(x, y, x2, y2);
x = x2;
y = y2;
}
});
function drawCircle(x, y) {
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.fill();
}
function drawLine(x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.strokeStyle = color;
ctx.lineWidth = size * 2;
ctx.stroke();
}
function updateSizeOnScreen() {
sizeEl.innerHTML = fakeSize;
}
increaseBtn.addEventListener("click", () => {
size += 5;
fakeSize++;
if (fakeSize > 10) {
fakeSize = 10;
}
if (size > 50) {
size = 50;
}
updateSizeOnScreen();
});
decreaseBtn.addEventListener("click", () => {
size -= 5;
fakeSize--;
if (fakeSize < 1) {
fakeSize = 1;
}
if (size < 5) {
size = 5;
}
updateSizeOnScreen();
});
colorEl.addEventListener("change", (e) => {
color = e.target.value;
});
clearEl.addEventListener("click", () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
});
//Eraser and Pencil Actions (my own algorithm)
const eraser = document.getElementById("eraser");
const pencil = document.getElementById("pencil");
eraser.addEventListener("click", () => {
localStorage.setItem("colorEl", JSON.stringify(color));
color = "#fff";
colorEl.disabled = true;
canvas.classList.add("eraseractive");
eraser.classList.add("eraseractive");
colorEl.classList.add("eraseractive");
canvas.classList.remove("pencilactive");
eraser.classList.remove("pencilactive");
colorEl.classList.remove("pencilactive");
});
pencil.addEventListener("click", () => {
JSON.parse(localStorage.getItem("colorEl"));
color = colorEl.value;
colorEl.disabled = false;
canvas.classList.remove("eraseractive");
eraser.classList.remove("eraseractive");
colorEl.classList.remove("eraseractive");
canvas.classList.add("pencilactive");
eraser.classList.add("pencilactive");
colorEl.classList.add("pencilactive");
});
// Dark/Light Mode
const darkMode = document.getElementById("darkMode");
const lightMode = document.getElementById("lightMode");
const toolbox = document.getElementById("toolbox");
darkMode.addEventListener("click", () => {
darkMode.classList.add("mode-active");
lightMode.classList.remove("mode-active");
lightMode.classList.add("rotate");
darkMode.classList.remove("rotate");
toolbox.style.backgroundColor = "#293462";
document.body.style.backgroundImage =
"url('/assets/images/darkModeBackground.svg')";
document.body.style.backgroundSize = "1920px 1080px";
canvas.style.borderColor = "#293462";
toolbox.style.borderColor = "#293462";
});
lightMode.addEventListener("click", () => {
lightMode.classList.add("mode-active");
darkMode.classList.remove("mode-active");
darkMode.classList.add("rotate");
lightMode.classList.remove("rotate");
toolbox.style.backgroundColor = "#293462";
document.body.style.backgroundImage =
"url('/assets/images/lightModeBackground.svg')";
document.body.style.backgroundSize = "1920px 1080px";
canvas.style.borderColor = "#293462";
toolbox.style.borderColor = "#293462";
});
* {
box-sizing: border-box;
font-size: 20px !important;
}
body {
background: url("https://drawing-app-green.vercel.app/assets/images/lightModeBackground.svg");
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
margin: 0;
position: relative;
max-height: 100vh;
overflow: hidden;
}
::selection {
background: transparent;
}
::-moz-selection {
background: transparent;
}
.mode {
display: flex;
position: absolute;
top: 10px;
right: 25px;
cursor: pointer;
}
.light-mode {
color: yellow;
}
.dark-mode {
color: #16213e;
}
.container {
display: flex;
flex-direction: column;
max-width: 1200px;
width: 100%;
max-height: 600px;
height: 100%;
}
canvas {
display: flex;
border: 2px solid #293462;
cursor: url("https://drawing-app-green.vercel.app/assets/images/pencilCursor.png") 2 48, pointer;
background-color: #fff;
margin-top: 3rem;
width: 100%;
height: 600px;
}
.toolbox {
background-color: #293462;
border: 1px solid #293462;
display: flex;
width: 100%;
align-items: center;
justify-content: center;
padding: 0.2rem;
}
.toolbox > * {
background-color: #fff;
border: none;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 2rem;
height: 30px;
width: 30px;
margin: 0.25rem;
padding: 0.25rem;
cursor: pointer;
}
.toolbox > *:last-child {
margin-left: auto;
}
canvas.eraseractive {
cursor: url("https://drawing-app-green.vercel.app/assets/images/eraserCursor.png") 2 48, pointer;
}
#color.eraseractive {
cursor: not-allowed;
}
canvas.pencilactive {
cursor: url("https://drawing-app-green.vercel.app/assets/images/pencilCursor.png") 2 48, pointer;
}
.mode-active {
visibility: hidden;
}
.rotate {
transform: rotate(360deg);
transition: transform 1s linear;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Drawing App</title>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"
integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
</head>
<body>
<i class="fa-solid fa-moon dark-mode fa-2x mode" id="darkMode"></i>
<i
class="fa-solid fa-sun light-mode fa-2x mode mode-active"
id="lightMode"
></i>
<div class="container">
<canvas id="canvas" width="1024" height="600"></canvas>
<div class="toolbox" id="toolbox">
<button id="decrease">-</button>
<span id="size">1</span>
<button id="increase">+</button>
<input type="color" id="color" />
<button id="pencil">
<img src="assets/images/pencilCursor.png" alt="" />
</button>
<button id="eraser">
<img src="assets/images/eraserCursor.png" alt="" />
</button>
<button id="clear">X</button>
</div>
</div>
<script src="assets/js/script.js"></script>
</body>
</html>
Your problem is that your canvas dimentions don't match with the dimentions of the HTML element that contains it. You see: your canvas has a fixed width="" and height="" attributes set. But in your HTML your canvas element has a width of 100%. So that means that the container vairies in dimentions but the canvas inside it not. This result in the canvas trying to resize to show inside the container thus giving you issues with calculating exacly what pixel you are clicking.
You have two options:
Option 1: calculate your click position taking into account canvas deformation
If you want your canvas to resize, then calculate the real position using a simple ratio formula. If for example your canvas has a width of 100 but right now its container is 10px wide, then if you click on pixel 5 you expect a dot to be drawn at pixel 50. In other words if your canvas is smaller by a factor of 10 then you need to multiply your position by a factor of 10.
In your code it would look something like this:
// this is your same code in lines 33 ana34 but see that I added a multiplication by the ratio between the canvas size and the canvas container
const x2 = e.offsetX * (canvas.width / ctx.canvas.getBoundingClientRect().width);
const y2 = e.offsetY * (canvas.height / ctx.canvas.getBoundingClientRect().height);
Option #2: Dont allow your canvas to deform
Remove the container class, and remove the width:100% from your canvas css. Your canvas will overflow and cause a scrollbar but the positions will be calculated properly with your code.

How to Handle Large image in Jcrop?

I am using Jcrop to crop Images, it is working fine with small size images, but Whenever I try to put large image like 2080x2080 or 1600x1600 it covers all my screen, also It cannot be handle with CSS like width and height control in Image tag
Seeking Solution/Suggestion -
Any method to put this image in container/div and then zoom-in/out with mouse event ?
Any method to handle large size image in a fix container and crop so that image maintain it's quality.
P.s: you can try with large image in below working example and see how it behaves with big Image.
var jcp;
var save = document.getElementById('save');
var imageLoader = document.getElementById('imageLoader');
var img = document.getElementById("target");
imageLoader.onchange = function handleImage(e) { //handling our image picker <input>:
var reader = new FileReader();
reader.onload = function(event) {
img.src = event.target.result;
}
reader.readAsDataURL(e.target.files[0]);
}
save.onclick = function() {
if (jcp && jcp.active) {
var i = 0;
for (area of jcp.crops) {
i++;
canvas = document.createElement("canvas");
canvas.setAttribute('width', area.pos.w);
canvas.setAttribute('height', area.pos.h);
ctx = canvas.getContext("2d");
ctx.drawImage(img, area.pos.x, area.pos.y, area.pos.w, area.pos.h, 0, 0, area.pos.w, area.pos.h);
temp = document.createElement('a');
temp.setAttribute('download', 'area' + i + '.jpg');
temp.setAttribute('href', canvas.toDataURL("image/jpg").replace("image/jpg", "image/octet-stream"));
temp.click();
}
}
};
Jcrop.load('target').then(img => {
jcp = Jcrop.attach(img, {
multi: true
});
});
body {
margin: 0px;
padding: 0px;
background-color: #ededed;
}
.cropapp {
position: absolute;
width: 100%;
height: 100%;
left: 5%;
background: lightgray;
}
.cropbox {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
}
div {
position: relative;
overflow: hidden;
}
<head>
<title>Jcrop Example</title>
<link href="https://unpkg.com/jcrop#3.0.1/dist/jcrop.css" rel="stylesheet" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://unpkg.com/jcrop#3.0.1/dist/jcrop.js"></script>
</head>
<body>
<div style="width:400px; margin:10px auto; padding:10px 0; overflow:hidden;"><a style="float:right; display:block; padding:10px 15px; color:#fff; background-color:#237dbd; font-size: 14px; font-weight:bold; border-radius:5px;" id="save">Save</a>
<input type="file" id="imageLoader" name="imageLoader" />
<!-- add this for file picker -->
</div>
<div>
<h1 style="font-family:Helvetica,sans-serif;">
Image Crop <span style="color:lightgray;"></span>
</h1>
<img id="target" style="background-size: cover !important;">
</div>
</body>
You can add any fixed width or height value to your <img id="target"/>. Just calculate and apply the change ratio to areas:
canvas = document.createElement("canvas");
displayWidth=img.width;
displayHeight=img.height;
realWidth = img.naturalWidth;
realHeight = img.naturalHeight;
widthRatio = Math.round(realWidth/displayWidth);
heightRatio = Math.round(realHeight/displayHeight);
width=area.pos.w*widthRatio
height=area.pos.h*heightRatio
canvas.setAttribute('width', width);
canvas.setAttribute('height', height);
ctx = canvas.getContext("2d");
ctx.drawImage(img, area.pos.x*widthRatio, area.pos.y*heightRatio, width, height, 0, 0, width, height);
and <img width="500" id="target"/> (or with css #target{width:500px})

Updating percentage in progress circle

I'm building a to-do list with a progress circle, using one of the alternatives given here (CSS Progress Circle). In my script.js I defined the function drawRingProgress() which renders the canvas when I execute it at the end of the script.
As the other functions of my script are executed to add tasks, edit, remove, or mark them as complete, the parameters pendingTasks and completedTasks get updated. However, if I call the function drawRingProgress() within the other mentioned functions, in order to update the progress, the canvas is wrongly drawn somewhere else multiple times (depending on the HTML elements these functions are acting on). What would be a correct approach to render the updated progress percentage?
Link to the working example: https://jsfiddle.net/tailslider13/f4qtmhzj/7/
let pendingTasks = 31;
let completedTasks = 69;
function drawRingProgress(pendingTasks, completedTasks) {
var el = document.getElementById('graph'); // get canvas
let progress_percentage = Math.floor((completedTasks / (completedTasks + pendingTasks)) * 100) || 0;
var options = {
// percent: el.getAttribute('data-percent') || 25,
percent: progress_percentage,
// size: 110,
size: el.getAttribute('data-size') || 220,
lineWidth: el.getAttribute('data-line') || 15,
rotate: el.getAttribute('data-rotate') || 0
}
var canvas = document.createElement('canvas');
var span = document.createElement('span');
span.textContent = options.percent + '%';
if (typeof (G_vmlCanvasManager) !== 'undefined') {
G_vmlCanvasManager.initElement(canvas);
}
var ctx = canvas.getContext('2d');
canvas.width = canvas.height = options.size;
el.appendChild(span);
el.appendChild(canvas);
ctx.translate(options.size / 2, options.size / 2); // change center
ctx.rotate((-1 / 2 + options.rotate / 180) * Math.PI); // rotate -90 deg
//imd = ctx.getImageData(0, 0, 240, 240);
var radius = (options.size - options.lineWidth) / 3.2;
var drawCircle = function (color, lineWidth, percent) {
percent = Math.min(Math.max(0, percent || 1), 1);
ctx.beginPath();
ctx.arc(0, 0, radius, 0, Math.PI * 2 * percent, false);
ctx.strokeStyle = color;
ctx.lineCap = 'round'; // butt, round or square
ctx.lineWidth = lineWidth
ctx.stroke();
};
drawCircle('#efefef', options.lineWidth, 100 / 100);
drawCircle('#046582', options.lineWidth, options.percent / 100)
}
drawRingProgress(pendingTasks, completedTasks);
Here is how I would draw the graph. I have removed all of the other functions from this so it is only showing the graph progress based on what you set the variables to. Once you get your other functions figured out you can updated them via that method.
First I would get the canvas at the beginning of the script and also designate the variables a global.
Second I would draw the white doughnut flat out. Unless you plan on changing it in some way the function drawGraph() will get called once and that's it.
Third the function drawRingProgress() will get called from your other functions when you add, delete, or complete a task. Be sure those function also update pendingTasks and completedTasks prior to calling drawRingProgress().
Inside drawRingProgress() I added the text since canvas has that built in method so you don't need to use a <span>. As far as all your options I removed them for this but you can add them back as where you see fit.
const inputField = document.getElementById("addTask");
const taskList = document.getElementById("taskList");
var canvas = document.getElementById('graph');
var ctx = canvas.getContext('2d');
canvas.width = 200;
canvas.height = 200;
let pendingTasks = 20;
let completedTasks = 5;
//Progress ring
function drawGraph() {
ctx.beginPath();
ctx.strokeStyle = "white";
ctx.arc(canvas.width/2, canvas.height/2, 50, 0, Math.PI*2, false);
ctx.lineCap = 'round'; // butt, round or square
ctx.lineWidth = 15;
ctx.stroke();
ctx.closePath();
}
drawGraph();
function drawRingProgress(pendingTasks, completedTasks) {
let progress_percentage = (completedTasks / pendingTasks) * 100;
ctx.font = "30px sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "#046582";
ctx.fillText(progress_percentage+'%', canvas.width/2,canvas.height/2);
percent = Math.min(Math.max(0, (progress_percentage/100) || 1), 1);
ctx.beginPath();
ctx.save();
ctx.translate(0, canvas.height); // change center
ctx.rotate((-1 / 2 + 0 / 180) * Math.PI); // rotate -90 deg
ctx.arc(canvas.width/2, canvas.height/2, 50, 0, Math.PI * 2 * percent, false);
ctx.strokeStyle = "#046582";
ctx.lineCap = 'round'; // butt, round or square
ctx.lineWidth = 15;
ctx.stroke();
ctx.restore();
ctx.closePath();
}
drawRingProgress(pendingTasks, completedTasks);
#body {
background-color: #046582;
}
header {
background-color: #f39189;
padding: 50px;
margin: 50px;
position: sticky;
top: 0px;
}
h1 {
text-align: center;
}
.listItem {
margin: 20px 0px;
background-color: white;
}
.container {
background-color: #c4c4c4;
}
.taskList {
list-style-type: none;
background-color: transparent;
overflow: hidden;
margin-top: 150px;
}
.inputContainer {
margin: 50px;
padding: 20px;
max-width: 50%;
width: 100%;
background-color: #f39189;
}
#footer {
text-align: center;
position: sticky;
bottom: 0px;
background-color: #f39189;
padding: 20px;
}
.deleteButton {
background-image: url("/content/delete.png");
background-repeat: no-repeat;
background-size: cover;
cursor: pointer;
width: 30px;
height: 30px;
margin: 15px;
}
#addTask {
font-family: Verdana, Geneva, Tahoma, sans-serif;
font-size: 1.3rem;
}
.taskName {
font-family: Verdana, Geneva, Tahoma, sans-serif;
font-size: 1.2rem;
}
.listContainer {
height: 1080px;
}
.inputContainer {
position: fixed;
}
.checkedbox {
text-decoration: line-through;
color: #f39189;
}
/* START Styling Progress ring */
.chart {
position: relative;
/* margin:0px; */
width: 220px;
height: 220px;
}
canvas {
display: block;
position: absolute;
top: 0;
left: 0;
}
span {
color: #046582;
display: block;
line-height: 220px;
text-align: center;
width: 220px;
font-family: sans-serif;
font-size: 30px;
font-weight: 100;
margin-left: 5px;
}
/* Links progress ring */
/* https://stackoverflow.com/questions/14222138/css-progress-circle
http://jsfiddle.net/Aapn8/3410/ */
/* END Styling Progress ring */
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<!-- Required meta tags -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0" crossorigin="anonymous" />
</head>
<div class="container">
<body id="body">
<header class="row justify-content-end">
<h1 class="col-4">Take note</h1>
<!-- Progress ring -->
<div class="col-4">
<canvas class="chart" id="graph"></canvas>
</div>
</header>
<!-- Input field and button -->
<div class="row inputContainer rounded">
<input class="col-auto" type="newTask" placeholder="Enter new Task" id="addTask" />
<button class="col-auto" id="btnAdd">Add</button>
</div>
<!-- List of tasks created -->
<div class="listContainer">
<ul class="taskList rounded" id="taskList"></ul>
</div>
<footer class="row" id="footer">
<h6 class="col w-100">2021</h6>
</footer>
<!-- BOOTSTRAP -->
<script src="https://cdn.jsdelivr.net/npm/#popperjs/core#2.9.2/dist/umd/popper.min.js" integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p" crossorigin="anonymous">
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.0.0/dist/js/bootstrap.min.js" integrity="sha384-lpyLfhYuitXl2zRZ5Bn2fqnhNAKOAaM/0Kr9laMspuaMiZfGmfwRNFh8HlMy49eQ" crossorigin="anonymous">
</script>
</body>
</div>
</html>
I also wasn't sure what you intent was with using bootstraps chart. I haven't used it before but from checking the docs it didn't appear you were actually coding appropriatly for it. Also you had a <div> with the class of chart and not a <canvas> which appeared wrong to me (but like I said I haven't used it before). In the example here I changed it to <canvas> and also got rid of the canvas you were creating along with the span.
Hopefully this is what you wanted if not maybe you can still piece together what I have here with what exactly you want.
Hey Carlos and everybody interested in a solution.
After investigating the code I noticed the problem lies in creating the elements span and canvas everytime the function gets invoked but never removed.
The solution to that is to have these elements in place to begin with, namely in the html code || or create them once before the function is called.
As for the variables pendingTasks and completedTasks, I would suggest changing them to pendingTasks and totalAmountOfTasks. (Unless there is a third state in which they can be.)
Then the ratio you would feed into the circle is pendingTasks/totalAmountOfTasks.
Remember to check for dividing by zero, when there are no tasks!
Cheers,
Thomas

How do I ensure canvas is contained in viewport

I have a canvas that is the only thing on a page I want really want to see. I'm trying to make it such that when I resize my window, I always have the entire canvas viewable with no scroll bars and maintain the aspect ratio.
MCVE
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let w = canvas.width;
let h = canvas.height;
ctx.fillStyle = 'teal';
ctx.fillRect(0, 0, w, h);
ctx.strokeStyle = 'red';
ctx.lineWidth = 10;
ctx.rect(5, 5, w - 5, h - 5);
ctx.stroke();
* {
background-color: white;
max-height: 100%;
max-width: 100%;
margin: 0;
padding: 0;
}
div#canvasDiv {}
canvas#canvas {
display: block;
margin: auto;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
<div id="canvasDiv">
<canvas id="canvas" width="500" height="500" />
</div>
</body>
</html>
All here at jsfiddle
With this attempt, it rescales just fine when I resize the width. However, when I resize the height, I get overflow.
Resize Width with appropriate rescaling
Resize Height with unwanted overflow
You may like to read this article: [Centering in CSS: A Complete Guide](https://css-tricks.com/centering-css-complete-guide/
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let w = canvas.width;
let h = canvas.height;
ctx.fillStyle = 'teal';
ctx.fillRect(0, 0, w, h);
ctx.strokeStyle = 'red';
ctx.lineWidth = 10;
ctx.rect(5, 5, w - 5, h - 5);
ctx.stroke();
* {
background-color: white;
max-height: 100%;
max-width: 100%;
margin: 0;
padding: 0;
}
canvas#canvas {
display: block;
margin: auto;
position:absolute;
top:0;bottom:0;
left:0;
right:0;
}
<div id="canvasDiv">
<canvas id="canvas" width="500" height="500"/>
</div>

I cannot draw the image in chrome correctly

Here simple code1 which work fine Demo jsFiddle ,I want the same result in code2
Here the code2 Demo jsfiddle which have two problems.
The scale is not correct to 1400x700 pix
I can only successes to draw from inside function loadImage()
function loadImage(){
oG.width = 1400;
oG.height = 700;
oG.drawImage(scrollImg,0,0);
}
I want to draw from scrollObject.draw()
var scrollObject = {
draw : function() {
oG.drawImage(scrollImg,0,0);
}
};
How can I do that ?
Many thanks.
code1;
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
#mycanvas{
border: 1px solid #9C9898;
}
</style>
</head>
<body >
<canvas id="mycanvas" width="1400" height="700" ></canvas>
<script type="text/javascript">
// get the canvas element using the DOM http://jsfiddle.net/centerwow/Z2UzF/show/
var context = document.getElementById('mycanvas').getContext("2d");
context.width = 1400 ;
context.height = 700 ;
var img = new Image();
img.src = "http://s9.postimage.org/433ecr79b/grid.png";
img.onload = function () {
context.clearRect(0,0,1400,700);
context.drawImage(img, 0, 0);
}
</script>
</body>
</html>
code2:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title> image not display correct</title>
<style type='text/css'>
body {
background-color:black;
}
.Container {
position: relative;
margin-left:auto;
margin-right:auto;
top:10px;
background-image:url('http://s8.postimage.org/jnu65wk2d/Box_Grass200x100.jpg');
background-repeat:repeat;
width:1000px;
height:500px;
z-index:0;
overflow:hidden;
}
#layer1{
position:absolute;
padding:0;
margin: 0px;
z-index:2;
top:-100px;
left:-200px;
width:1400px;
height:700px;
}
</style>
</head>
<body >
<div class="Container">
<canvas id="layer1">LAYER1</canvas>
</div>
<script type='text/javascript'>//http://jsfiddle.net/centerwow/Z2UzF/1/show/
function loadImage(){
oG.width = 1400;
oG.height = 700;
oG.drawImage(scrollImg,0,0);
}
//layer 1 image then will be object objectGame = oG
var oG = document.getElementById('layer1').getContext("2d");
scrollImg = new Image();
scrollImg.src = "http://s9.postimage.org/433ecr79b/grid.png"; //image size 1400x700px
scrollImg.onload = loadImage;
var scrollObject = {
// Basic attributes
draw : function() {
oG.drawImage(scrollImg,0,0);
}
};
// Now moving it
function move() {
//handle with object background
//clearRect(x, y, width, height)
oG.clearRect(0,0,1400,700);
scrollObject.draw();
}
move();
</script>
</body>
</html>
You can only draw on loadImage(), because when you trigger move() the image have not been loaded yet. You can use a assetmanager to download your assets like images, sounds etc. Here is a great guide to build one - http://www.html5rocks.com/en/tutorials/games/assetmanager/
And for the scale, you can pass a width and height argument to the drawImage function.
context.drawImage(image, x, y, width, height);
You are also setting the height and width of the 2d-context, that you cannot do. Set the width and height in the <canvas> attributes. I once had problems with height/width when I used css, so I would not recommend setting the width/height there.

Categories

Resources