I have an image (well data) that is m = 3600 n = 512. It has to stay like this. The data really starts as an array but is then broken down into m,n chunks. I would like to change the aspect ratio in canvas. If I change canvas.width, it gives me more available space that I don't use. If I change canvas.style.width, it increases both height and width.
I want the image to be wider while keeping its same length.
<!doctype html>
<html class="no-js" lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Font Awesome -->
<link
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.css"
rel="stylesheet"
/>
<!-- Google Fonts -->
<link
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
rel="stylesheet"
/>
<!-- MDB -->
<link
href="https://cdnjs.cloudflare.com/ajax/libs/mdb-ui-kit/3.11.0/mdb.min.css"
rel="stylesheet"
/>
<!-- MDB -->
<script
type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/mdb-ui-kit/3.11.0/mdb.min.js"
></script>
<style>
body {
max-width: 910px;
margin: 0 auto;
background-color:#FFFFFF;
text-align:center;
}
</style>
</head>
<body>
<label class="form-label" for="customRange1">X frequency</label>
<div class="range">
<input type="range" class="form-range" id="customRange1" />
</div>
<label class="form-label" for="customRange2">Y frequency</label>
<div class="range">
<input type="range" class="form-range" id="customRange2" />
</div>
<canvas style="outline: black 2px solid;" id="leftcanvas" ></canvas>
<script>
function pixel(array){
var inc = 0;
var buffer = new Uint8ClampedArray(width * height * 4); // have enough bytes
for(var y = 0; y < height; y++) {
for(var x = 0; x < width; x++) {
var pos = (y * width + x) * 4; // position in buffer based on x and y
buffer[pos ] = 0; // some R value [0, 255]
buffer[pos+1] = 0; // some G value
buffer[pos+2] = 0; // some B value
buffer[pos+3] = array[inc]; // set alpha channel
inc++
}
}
return buffer;
};
function makecanvas(idtag,height,imgdata){
var canvas = document.getElementById(idtag),
ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = false;
canvas.height = height;
canvas.width = width;
/* THIS PART I CANT FIGURE OUT */
/* increases canvas but not image */
canvas.width = width*1.25;
/* increases width and length */
//canvas.style.width = 1.25* Math.round(512) + "px";
var idata = ctx.createImageData(width, height);
idata.data.set(imgdata);
ctx.putImageData(idata, 0, 0);
return [idata,ctx];
};
function freq(hzx,hzy){
data = [];
for (var y = 0; y < 3600; y+= 1){
for (var x = 0; x < 512; x+= 1){
data.push(Math.round((255/4*(Math.sin(hzy*2*Math.PI*y/3600) + Math.sin(hzx*2*Math.PI*x/511)+2))))
}
}
return data;
}
trace = freq(1,1)
var width = 512,
height = trace.length/512;
imgmatrix = pixel(trace);
CI = makecanvas("leftcanvas",height,imgmatrix);
var idata = CI[0];
const ictx = CI[1];
var slideX = document.getElementById('customRange1');
var slideY = document.getElementById('customRange2');
slideX.onchange = function() {
hzx = slideX.value;
hzy = slideY.value;
trace = freq(hzx,hzy)
imgmatrix = pixel(trace);
idata.data.set(imgmatrix);
ictx.putImageData(idata, 0, 0);
}
slideY.onchange = function() {
hzx = slideX.value;
hzy = slideY.value;
trace = freq(hzx,hzy)
imgmatrix = pixel(trace);
idata.data.set(imgmatrix);
ictx.putImageData(idata, 0, 0);
}
</script>
</body>
</html>
I'm almost afraid to answer - but what about the most obvious solution:
Instead of just changing the width of the canvas via CSS - change both width & height.
canvas.style.width = 1.2 * Math.round(512) + "px";
canvas.style.height = "3600px";
Related
I'm trying to create a wobbly circle with multiple ellipses in the background, however I can't add the for loop to the draw function otherwise it will be running in every frame, and if I add it to setup then the wobbly circle leaves a trace. And if I use clear() then it removes my background. How can I solve this, please?
function setup() {
createCanvas(600, 600);
for (var i = 0; i < 1000; i++){
fill(255);
ellipse(random(0, width), random(0, width), random(5, 5), random(5, 5));
}
x = width/2; // Circle center x
y = height/2; // Circle center y
r = 100; // Circle radius
verts = 200; // Number of vertices for drawing the circle
wobbleSlider = createSlider(0, 200, 100, 1); // How much the circle radius can vary
smthSlider = createSlider(1, 500, 70, 1); // How smooth the noise function is (higher is smoother)
t = 1;
}
function draw() {
let wobble = wobbleSlider.value()
let smth = smthSlider.value();
//clear();
t += 0.01
beginShape()
for(let i = 0; i < verts; i++){
let f = noise(50*cos(i/verts*2*PI)/smth + t, 50*sin(i/verts*2*PI)/smth + t)
vertex(x + (r+wobble*f) * cos( (i/verts) * 2 * PI ), y + (r+wobble*f) * sin( (i/verts) * 2 * PI ) )
}
endShape(CLOSE)
}
html, body {
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<script src="sketch.js"></script>
</body>
</html>
enter image description here
What you want is some sort of layering system that allows you to have a fixed background and draw over it things that chage. You can achieve this with graphics. It basically allows you to create an independent "canvas" and draw over it, then you can render it like an image in your animation.
First you use createGraphics which is similar to createCanvas with the difference that you can store the result in a variable like: let myGraphics = createGraphics(600,600). Then you draw over it like in your canvas. For example ellipse(...) becomes myGraphics.ellipse(...). Then you can render it using image like image(myGraphics, 0, 0).
See the working code below
If this answers your question dont forget to mark it as the answer using the green check at the left of the answer so others can reference from it in the future :)
let background;
function setup() {
createCanvas(600, 600);
background = createGraphics(600, 400);
for (var i = 0; i < 1000; i++){
background.fill(255);
background.ellipse(random(0, width), random(0, width), random(5, 5), random(5, 5));
}
x = width/2; // Circle center x
y = height/2; // Circle center y
r = 100; // Circle radius
verts = 200; // Number of vertices for drawing the circle
wobbleSlider = createSlider(0, 200, 100, 1); // How much the circle radius can vary
smthSlider = createSlider(1, 500, 70, 1); // How smooth the noise function is (higher is smoother)
t = 1;
}
function draw() {
clear();
image(background,0,0);
let wobble = wobbleSlider.value()
let smth = smthSlider.value();
//clear();
t += 0.01
beginShape()
for(let i = 0; i < verts; i++){
let f = noise(50*cos(i/verts*2*PI)/smth + t, 50*sin(i/verts*2*PI)/smth + t)
vertex(x + (r+wobble*f) * cos( (i/verts) * 2 * PI ), y + (r+wobble*f) * sin( (i/verts) * 2 * PI ) )
}
endShape(CLOSE)
}
html, body {
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<script src="sketch.js"></script>
</body>
</html>
I wrote some code for image pixel manipulation. However, I found a strange issue. After loading a image from a local file, I have to add a short "sleep" before loading the image into a canvas, or the previous image (Firefox) / a null will be loaded into the canvas(Edge). Can someone tell me why this happened? Thanks. Code is here:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Image Grey Scale Noise</title>
</head>
<body>
<label for="Selector">Select Image</label>
<br />
<input type="file" id="Selector" multiple=false accept="image/*">
<br />
<br />
<label for="greyScale">Grey Scale</label>
<br />
<input type="range" id="greyScale" min="0.1" max="10" value="4" step="0.1">
<p>Image:</p>
<img id="srcImg" crossOrigin="" src="https://avatars2.githubusercontent.com/u/67770686?s=400&u=24015d89e8fab0fb45cad0be3d282f6062e45b5b&v=4" />
<br />
<img id="outImg" crossOrigin="" src="" />
<script>
const fileSelector = document.getElementById('Selector');
const greyScaleSelector = document.getElementById('greyScale')
const srcImg = document.getElementById('srcImg');
const outImg = document.getElementById('outImg');
var c = document.createElement("Canvas");
var ctx = c.getContext("2d");
async function getNewImage(result, greyScale) {
srcImg.src = result;
await new Promise(r => setTimeout(r, 100)); //Without this, the previous image will be loaded...
c.height = srcImg.height;
c.width = srcImg.width;
ctx.drawImage(srcImg, 0, 0);
var imageData = ctx.getImageData(0, 0, c.width, c.height);
const data = imageData.data
var l = data.length;
for (i = 0; i < l; i += 4) {
var r = Math.random() / greyScale + (1 - 1 / greyScale) //Grey Scale Change
data[i] = Math.min(data[i] * r, 255);
data[i + 1] = Math.min(data[i + 1] * r, 255);
data[i + 2] = Math.min(data[i + 2] * r, 255);
}
ctx.putImageData(imageData, 0, 0);
outImg.src = c.toDataURL("image/png");
}
fileSelector.addEventListener('change', async (event) => {
var greyScale = greyScaleSelector.value;
var result = URL.createObjectURL(fileSelector.files[0]);
await getNewImage(result, greyScale);
URL.revokeObjectURL(result);
})
greyScaleSelector.addEventListener('change', async (event) => {
var greyScale = greyScaleSelector.value;
var result = srcImg.src;
await getNewImage(result, greyScale);
})
</script>
</body>
</html>
Canvas iterator supposed to randomize a 20px^2 square between yellow and green across the canvas but for some unknown reason, the pixels are wider than squares until about halfway through the canvas, where the pixels fragment into very tall columns.
The expected result is to have a canvas filling the screen made up of 20x20pixel squares of random yellow or green color, which the canvas automatically scales to change the size and aspect ratio to accommodate window resizing.
The actual result is the canvas seems to produce a third of the screen as very short and wide pixels with the expected color pattern followed below by very tall columns, followed by the page behind all of this.
I've asked around elsewhere to no avail, and I'm looking for any solution that meets the desired goal, so I'm open to anything.
Source replit
snippet:
var has = false
var hasLoaded = false
console.log(window.innerHeight)
console.log(window.innerWidth)
console.log(window.innerWidth*1.5)
var has = false
function toggle() {
console.log("" + has.toString())
if(has){
has = false
document.getElementById('cx').classList.remove('filtered')
}else{
has = true
document.getElementById('cx').classList.add('filtered')
}
}
function resize() {
//window resize event to find canvas size
var h = window.innerHeight
var w = window.innerWidth
var p = 20
var ph = Math.floor(h/p)
var pw = Math.floor(w/p)
let refcx = document.getElementById('cx')
refcx.width = pw
console.log("resized")
dynDraw(w, h, pw, ph, p);
}
function dynDraw(w, h, pw, ph, p) {
//wha
//for drawing after resize event
console.log(w + "w, "+h+"h, "+pw+"pw, "+ph+"ph")
var c = document.getElementById("cx");
var ctx = c.getContext("2d");
ctx.imageSmoothingEnabled = false
//ctx.fillStyle = "#ff0000"
//ctx.fillRect(20, 20, 150, 100);
ctx.fillStyle = "#00ff00"
for(let row = 0; row <= ph; row++)
{ //each row accross width lines, ph stacks
for(let i = 0; i <= pw; i++)
{ //each pixel across height lines, pw stacks
Math.random() >= 0.5 ? ctx.fillStyle = "#fff000" : ctx.fillStyle = "#00ff00"
ctx.fillRect(i, row, pw, pw)
console.log(i + "," + row + "," + pw + "," + ph)
}
}
console.log("done")
}
#cx {
position: fixed;
top: 0px;
left: 0px;
height: 100vh;
width: 100vw;
z-index: -5;
}
.filtered {
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-crisp-edges;
image-rendering: pixelated;
image-rendering: crisp-edges;
}
<!DOCTYPE html>
<html>
<head>
<meta charset = "utf-8">
<meta name = "viewport" content = "width = device-width">
<title>Canvas Testing</title>
<link href = "style.css" rel = "stylesheet"></link>
<script src = "script.js"></script>
</head>
<body onload="resize();" >
<button id="buttonToDoStuff" onclick="toggle();">toggle filter</button>
<canvas id="cx" width="100" height="100">
Your browser does not support the HTML5 canvas tag.
</canvas>
</body>
</html>
This line
ctx.fillRect(i, row, pw, pw)
should probably be
ctx.fillRect(i, row, 1, 1)
instead.
You also need to set both width and height in the resize handler. You only set width.
The issue is because you've sized the canvas draw area (the context) such that each square you want is 1x1 relative to the context dimensions. The <canvas>'s width and height attributes control the canvas element and the canvas drawing context dimensions. The CSS width and height override the element dimensions but do not effect the drawing context dimensions, the drawing context is instead stretched to fit the element.
var has = false
var hasLoaded = false
console.log(window.innerHeight)
console.log(window.innerWidth)
console.log(window.innerWidth*1.5)
var has = false
function toggle() {
console.log("" + has.toString())
if(has){
has = false
document.getElementById('cx').classList.remove('filtered')
}else{
has = true
document.getElementById('cx').classList.add('filtered')
}
}
function resize() {
//window resize event to find canvas size
var h = window.innerHeight
var w = window.innerWidth
var p = 20
var ph = Math.floor(h/p)
var pw = Math.floor(w/p)
let refcx = document.getElementById('cx')
refcx.width = pw
refcx.height = ph
console.log("resized")
dynDraw(w, h, pw, ph, p);
}
function dynDraw(w, h, pw, ph, p) {
//wha
//for drawing after resize event
console.log(w + "w, "+h+"h, "+pw+"pw, "+ph+"ph")
var c = document.getElementById("cx");
var ctx = c.getContext("2d");
ctx.imageSmoothingEnabled = false
//ctx.fillStyle = "#ff0000"
//ctx.fillRect(20, 20, 150, 100);
ctx.fillStyle = "#00ff00"
for(let row = 0; row <= ph; row++)
{ //each row accross width lines, ph stacks
for(let i = 0; i <= pw; i++)
{ //each pixel across height lines, pw stacks
Math.random() >= 0.5 ? ctx.fillStyle = "#fff000" : ctx.fillStyle = "#00ff00"
ctx.fillRect(i, row, 1, 1)
console.log(i + "," + row + "," + pw + "," + ph)
}
}
console.log("done")
}
#cx {
position: fixed;
top: 0px;
left: 0px;
height: 100vh;
width: 100vw;
z-index: -5;
}
.filtered {
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-crisp-edges;
image-rendering: pixelated;
image-rendering: crisp-edges;
}
<!DOCTYPE html>
<html>
<head>
<meta charset = "utf-8">
<meta name = "viewport" content = "width = device-width">
<title>Canvas Testing</title>
<link href = "style.css" rel = "stylesheet"></link>
<script src = "script.js"></script>
</head>
<body onload="resize();" >
<button id="buttonToDoStuff" onclick="toggle();">toggle filter</button>
<canvas id="cx">
Your browser does not support the HTML5 canvas tag.
</canvas>
</body>
</html>
I am trying to build a webpage that will allow a user to display a dungeon map from images stored in an array (directly or by reference) to a block. Suffice to say I am not having a lot of luck and I have researched until I am blue in the face. Can someone please let me know what I'm doing wrong...
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Dungeons and Dragons 3.5 Map Generator</title>
<script src="GenerateMaps.js"></script>
<link rel='stylesheet'>
<link rel="stylesheet" type="text/css" href="index.css">
</head>
<body onload="runExperiments();">
<div id="row">
<div class="page_items" id="map">
<script>
window.onload=function() {
let c = document.getElementById("map");
let ctx = c.getContext("2d");
let x;
let y;
c.width = (window.innerWidth / 2)-(window.innerWidth % 30);
c.height = (window.innerHeight)-(window.innerHeight % 30);
ctx.width = c.width - 30;
ctx.height = c.width - 30;
let elem = document.createElement("image");
elem.setAttribute("src", "floor.png");
elem.setAttribute("height", "30");
elem.setAttribute("width", "30");
elem.setAttribute("src", "floor.png");
for (x = 30; x < ctx.height; x += 30) {
for (y = 30; y < ctx.width; y += 30) {
c.appendChild(elem);
}
}
}
</script>
</div>
</div>
</body>
You might have other problems, but the immediate one is highlighted if you look at the Console of the developer tools in your browser.
Uncaught TypeError: c.getContext is not a function
at window.onload
getContext is a function found on canvas elements but you are trying to call it on a div.
Here's my html document:
<html>
<head>
<meta charset="utf-8">
<title>Automapper Editor</title>
</head>
<body>
<nobr>
<script>
var img = new Image()
img.src = "grass_main_0.7.png"
for(var y=0; y < 16; y++) {
for(var x=0; x < 16; x++) {
var tilecanvas = document.createElement("canvas")
var tilectx = tilecanvas.getContext("2d")
tilecanvas.width = 64
tilecanvas.height = 64
tilecanvas.draggable = true
tilectx.drawImage(img, x*64, y*64, 64, 64, 0, 0, 64, 64)
document.body.appendChild(tilecanvas)
}
document.body.appendChild(document.createElement("br"))
}
</script>
</nobr>
</body>
</html>
I want to split an image with the size of 1024 pixel in 16 images with the size of 64 pixel. Then i want to draw them on canvas and write them to the document. This are my problems:
The line tilecanvas.draggable = true doesn't work, this should do the same as <canvas draggable="true">.
The <nobr> tag doesn't prevent the canvas from wrapping, but i want them to stay 16x16 on the screen.
The first time you load the page you can't see the image, you can simulate this by pressing STRG+F5 (a.k.a. Ctrl-F5) in firefox.
You can solve it by:
Using a container div for the tiles
Set fixed width for container element
Adjust the CSS for canvas element to float
Live example
(note: added random alpha for the tiles/demo to increase visibility)
var img = new Image();
img.onload = render;
img.src = "http://www.psdgraphics.com/file/colorful-triangles-background.jpg";
function render() {
var cont = document.getElementById("cont");
for (var y = 0; y < 16; y++) {
for (var x = 0; x < 16; x++) {
var tilecanvas = document.createElement("canvas");
var tilectx = tilecanvas.getContext("2d");
tilecanvas.width = 64;
tilecanvas.height = 64;
tilecanvas.draggable = true;
tilectx.globalAlpha = Math.random() + 0.5; // just to increase visuals
tilectx.drawImage(img, x * 64, y * 64, 64, 64, 0, 0, 64, 64);
cont.appendChild(tilecanvas);
}
}
}
#cont {width: 1024px;border: 1px solid #000}
canvas {float:left}
<div id="cont"></div>
As said by RienNeVaPlu͢s & Ken Fyrstenberg, make sure you give your image time to load with onload.
Here's a proof-of-concept allowing html drag-drop of a spliced image-canvases:
https://jsfiddle.net/m1erickson/g9nfuved/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<style>
body{ background-color: ivory; padding:10px; }
canvas{border:1px solid red; margin-left:0px;}
#dropzone{width:250px;height:50px;border:1px solid blue;}
</style>
<script>
$(function(){
var $results=$('#results');
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/facesSmall.png";
function start(){
var w=img.width/4;
var h=img.height;
for(var x=0; x < 4; x++) {
var tilecanvas = document.createElement("canvas");
var tilectx = tilecanvas.getContext("2d");
tilecanvas.width = w;
tilecanvas.height = h;
tilecanvas.draggable = true
tilecanvas.id='canvas'+x;
tilectx.drawImage(img, x*w,0,w,h, 0, 0, w, h)
document.body.appendChild(tilecanvas)
tilecanvas.addEventListener('dragstart', function(e){
e.dataTransfer.setData('text',e.target.id);
}, false);
tilecanvas.addEventListener('dragenter', handleEvents, false);
tilecanvas.addEventListener('dragover', handleEvents, false);
tilecanvas.addEventListener('dragleave', handleEvents, false);
tilecanvas.addEventListener('dragend', handleEvents, false);
}
document.body.appendChild(document.createElement("br"))
var dropzone=document.getElementById('dropzone');
dropzone.addEventListener('dragover',function(e){e.preventDefault(); return(false);});
dropzone.addEventListener('dragenter',function(e){return(false);});
dropzone.addEventListener('drop',function(e){
$results.text('You dropped: '+e.dataTransfer.getData('text'));
return(false);
});
}
function handleEvents(e){ return(false); }
}); // end $(function(){});
</script>
</head>
<body>
<h4 id='results'>Drag 1+ of the image strips</h4>
<div id='dropzone' droppable='true'>Drop Here.</div>
</body>
</html>