I have two canvases, let's say Canvas A and Canvas B. The user uploads a file and it gets put on Canvas A, which I already have done. Once that happens, I want to start copying sections of Canvas A to Canvas B.
I have tried doing this
source = a.getContext('2d');
b.drawImage(source, 0, 0, source.width, source.height);
but that copies the whole thing. So my question is, how do I copy a part of Canvas A to Canvas B.
Edit:
I have also tried...
var imgData=atx.getImageData(10,10,20,20);
btx.putImageData(imgData, 0, 0);
but that just copies the imgData back onto Canvas A at (0, 0).
To start with, b.drawImage takes a CanvasImageSource as its first argument, not a Context. Pass your entire HTMLCanvasElement in there.
drawImage takes nine arguments, and you're gonna need all of them. If you only use five, it's assumed that you're taking the entire source image and drawing it at your destination. The full signature is
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);
sx, sy, sw, and sh define the rectangle you're copying from your source. dx, dy, dw, and dh define the rectangle you're drawing in at your destination. Note that sw and sh don't have to match up with dw and dh. If the source and destination rectangles don't have the same size, the copied image will be squashed or stretched to fit the destination rectangle.
I've made a snippet that lets you play around with drawImage to see what it's doing. It works best when you view it as a full page:
const srcCanvas = document.querySelector('#source');
const srcContext = srcCanvas.getContext('2d');
const destContext = document.querySelector('#destination')
.getContext('2d');
// The overlay canvas is where we draw the red rectangle. It's
// positioned directly on top of the source canvas.
const overlay = document.querySelector('#overlay')
.getContext('2d');
overlay.fillStyle = 'rgba(255, 0, 0, 0.3)';
overlay.strokeStyle = 'red';
const inputs = Array.from(
document.querySelectorAll('input[type="number"]')
);
const lockScale = document.querySelector('#lockScale');
lockScale.addEventListener('change', () => {
inputs[6].disabled = lockScale.checked;
inputs[7].disabled = lockScale.checked;
if (lockScale.checked) update();
});
// Grab an image and draw it on the source canvas...
fetch('https://picsum.photos/320/240/?image=451')
.then(res => res.blob())
.then(createImageBitmap)
.then(bitmap => {
srcContext.drawImage(bitmap, 0, 0);
// ...and only then start watching for changes in the input boxes.
// There's no point in spending cycles copying an empty canvas.
inputs.forEach(i => {
i.addEventListener('input', update);
});
update();
});
function update() {
if (lockScale.checked) {
inputs[6].value = inputs[2].value;
inputs[7].value = inputs[3].value;
}
const values = inputs.map(i => Number(i.value));
destContext.clearRect(0, 0, 320, 240);
overlay.clearRect(0, 0, 320, 240);
overlay.beginPath();
overlay.rect(
// These adjustments move the overlay path off the boundary
// between pixels so the rectangle border is a crisp 1px line.
values[0] + 0.5,
values[1] + 0.5,
// JavaScript uses half-open intervals, which makes sense for
// code. But for a visualization of graphics work, fully-
// closed intervals are preferable. These adjustments make the
// overlay rectangle exactly cover the pixels that will be
// copied.
values[2] - 1,
values[3] - 1
);
overlay.fill();
overlay.stroke();
// The real drawing code of this snippet doesn't look like the
// code you would actually use. We call apply() on drawImage()
// so we can pass in the entire values array without listing out
// every element.
destContext.drawImage.apply(
destContext, // We don't want to change what 'this' points to
[srcCanvas].concat(values));
// The spread operator provides a more elegant way of doing this:
// destContext.drawImage(srcCanvas, ...values);
// But it doesn't work with IE.
}
canvas {
height: 240px;
width: 320px;
position: absolute;
top: 0;
left: 0;
}
.canvasbox {
display: inline-block;
position: relative;
width: 320px;
height: 240px;
border: 1px solid black;
}
input[type="number"] {
font-family: monospace;
border: none;
background: silver;
color: black;
width: 3em;
}
input[disabled] {
color: silver;
background: grey;
}
code {
color: navy;
}
<div class="canvasbox">
<canvas id="source" width="320" height="240"></canvas>
<canvas id="overlay" width="320" height="240"></canvas>
</div>
<div class="canvasbox">
<canvas id="destination" width="320" height="240"></canvas>
</div>
<div>
<code>
destContext.drawImage(srcCanvas,
<input id="sx" type="number" value="180">,
<input id="sy" type="number" value="100">,
<input id="sw" type="number" value="40">,
<input id="sh" type="number" value="50">,
<input id="dx" type="number" value="10">,
<input id="dy" type="number" value="10">,
<input id="dw" type="number" value="40">,
<input id="dh" type="number" value="50">);
</code>
</div>
<div>
<label>
<input id="lockScale" type="checkbox">Lock scale
</label>
</div>
Related
I am trying to display parts from an image sheet into separate images.
There's a way to do it with CSS, as in how Reddit Flairs work on the old version of Reddit.
Below is an image that acts as a sheet. I am going to call it unicode_page_00.png
body {
background-color: rgb(20, 20, 20);
}
<body>
<div>
<div style="background: url(https://i.stack.imgur.com/GxJOx.png); image-rendering: pixelated; width: 64px; height: 64px; background-size: 1024px; background-position: -64px 0px;">
</div>
</div>
<br>
<div>
<img src="https://i.stack.imgur.com/GxJOx.png">
</div>
</body>
https://jsfiddle.net/0ztpr5cq/1/
The first part displays an image taken from the image sheet. For this example, the sheet is 256x256 pixels and each character is 16x16 pixels.
I took it and zoomed it in by 4. So the first image shows as 64x64. Then I've shifted the position to display the second character on the sheet from left to right.
And below is the entire image sheet for demonstration.
This is done with CSS. The issue is that you can not save the image the way it was cropped and modified with CSS. Even if it would be made possible with CSS, it would take the entire image sheet and save that instead.
I am trying to display every character on the image sheet as separate images. That you could then freely zoom in with the pixelated effect.
The idea that I have is to turn them into Base64 or something and make the modifications there or before. And at the end, display the final result back into an image, as base64 or blob or other. And that all with the help of JavaScript.
But how would it work? What would the actual process be?
A good solution I came up with was this https://jsfiddle.net/1rmya08u/
You would have to use the canvas element. Here's the solution:
<canvas id="canvas" width="64" height="64"></canvas>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.imageSmoothingEnabled = false; // For pixelated drawing
var sheet = new Image();
sheet.src = "https://i.stack.imgur.com/GxJOx.png";
sheet.onload = function () {
var sx = 0;
var sy = 0;
var sWidth = 16;
var sHeight = 16;
var dx = 0;
var dy = 0;
var dWidth = 64;
var dHeight = 64;
ctx.drawImage(sheet, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
};
We're taking a 16x16 sample from the image sheet and rendering it at 64x64. For a better understanding of these arguments, take a look at the reference for ctx.drawImage()
And finally, you can get the result as an image with HTMLCanvasElement.toDataURL()
I'm working on a project and I use createCapture(VIDEO); using p5js libraries, to get the video using my web camera. Then I store every image of that capture (from < video> element) and draw it in a canvas in the function draw().
I was wondering if it's possible the change the shape of the canvas(such as a mirror for example (oval or circle)).
Again, notice that I don't want to crop the image but the canvas itself.
Here is some code:
function setup(){
canvas = createCanvas(640, 480); //480p
canvas.parent('editedCanvas-container');
originalCapture = createCapture(VIDEO);
originalCapture.parent('originalVideo-container');
originalCapture.size(640, 480); //480p
}
function draw(){
frame = image(originalCapture, 0, 0, width, height);
}
If somebody has a clue please let me know.
You can change the shape of your canvas tag by applying css rules,
So try this css code
#canvasObject {
border: 2px solid black;
width: 200px;
height: 150px;
border-radius: 100px / 50px;
}
<canvas id="canvasObject">
</canvas>
For more info about how to achieve shapes in css, please refer this amazing post for css-tricks
I'm currently exploring with Three.js, and working on a small project of mine.
The project consists on having a canvas focused on 3D models and animations, and another one behind, which handles the simpler 2D work.
I've set up the 3D canvas properly, so it's background is transparent, and I can see boxes I draw manually on the 2D canvas, which leads me to assume the setup is right.
The issue I'm having is when it comes to images. I simply cannot get an image to display on the 2D canvas. I've experimented on a separate project, and could draw Images there, no problem. The code is pretty basic, and I actually found it here, but is as follows:
window.onload = function() {
var canvas = document.getElementById('bgcanvas');
var context = canvas.getContext('2d');
var logoText = new Image();
logoText.onload = function() {
context.drawImage(logoText, 69, 50);
};
logoText.src = 'images/logotext.png';
}
#canvas {
position: fixed;
z-index: 0;
}
#bgcanvas {
z-index: -10;
position: fixed;
width: 100vw;
height: 100vh;
}
<div id="fixedContainer">
<canvas id="bgcanvas"></canvas>
<canvas id="canvas"></canvas>
</div>
What's going on that I'm unaware of?
Massive thanks in advance!
UPDATE EDIT: The issue was that I had an image on which the top left corner was transparent, and didn't know the image would stretch. user3412847's comment helped me figure it out
Specifying image width and height is a good habit to get into. Use this syntax: context.drawImage(image, x, y, width, height).
Hope this helps.
I'm guessing you don't have an image at that path; It works fine for me with a valid image (eg: http://lorempixel.com/100/100):
window.onload = function() {
var canvas = document.getElementById('bgcanvas');
var context = canvas.getContext('2d');
var logoText = new Image();
logoText.onload = function() {
context.drawImage(logoText, 69, 50);
};
logoText.src = 'http://lorempixel.com/100/100';
}
#canvas {
position: fixed;
z-index: 0;
}
#bgcanvas {
z-index: -10;
position: fixed;
width: 100vw;
height: 100vh;
}
<div id="fixedContainer">
<canvas id="bgcanvas"></canvas>
<canvas id="canvas"></canvas>
</div>
One of my interface elements is being rendered using the HTML5 <canvas> element and associated JavaScript API. This element is used in several places on the same screen and on multiple screens throughout the app. What is the most efficient way to display this everywhere it's required?
My first idea is to draw to a master canvas, which I then clone and insert where needed in the page. The master canvas might be something like:
var master = $('<canvas>').attr({
width: 100,
height: 100
}),
c = master[0],
ctx = c.getContext("2d");
ctx.fillStyle = "#FF0000";
ctx.fillRect(0, 0, 150, 75);
Let's say I want to duplicate the canvas in these div containers:
<div class="square-container" id="square_header"></div>
...
<div class="square-container" id="square_dataTable"></div>
...
<div class="square-container" id="square_gallery"></div>
....
When the page loads, I'll do this to insert a duplicate canvas element into each container:
$(document).ready(function() {
$('.square-container').each(function() {
master.clone().appendTo($(this));
});
});
The content being rendered on the canvas is going to be more complex than the simple square used in this example but will still end up being just a static image. It is possible, though, that there could be dozens of different images each cloned dozens of times per page.
The other approach I had in mind was to create an image using the toDataURL() method and set that as the appropriate images' sources:
var master = $('<canvas>').attr({
width: 100,
height: 100
}),
c = master[0],
ctx = c.getContext("2d");
ctx.fillStyle = "#FF0000";
ctx.fillRect(0,0,150,75);
var square = c.toDataURL('image/png');
I would add image tags where necessary:
<img src="" id="square_header" class="square" alt="" />
...
<img src="" id="square_dataTable1" class="square" alt="" />
...
<img src="" id="square_gallery" class="square" alt="" />
....
And then set all of their SRCs to that newly created image:
$(document).ready(function() {
$('img.square').attr('src', square);
});
To me, it pretty much looks like six of one, half dozen of the other. But I'm wondering if one way is considered better practice than the other? If the content being rendered on the <canvas> were more complex, would one way be more efficient than the other?
In that same spirit, when I need to use that element on subsequent pages, is it best to execute all the javascript (from whatever solution is deemed best above) on each page or would saving the value of CANVAS_ELEMENT.toDataURL() in a cookie and then using that on subsequent pages be any more efficient?
Cloning a canvas will duplicate its dimensions and styling, but not its image data. You can copy the image data by calling drawImage on the context. To paint the contents of originalCanvas onto duplicateCanvas, write:
duplicateCanvas.getContext('2d').drawImage(originalCanvas, 0, 0);
As a demonstration, the following snippet generates four canvases:
an original canvas with a small scene painted onto it
a copy made by calling cloneNode only
a copy made by calling cloneNode and drawImage
a copy made by creating a new image and setting its source to the data URI
function message(s) {
document.getElementById('message').innerHTML += s + '<br />';
}
function timeIt(action, description, initializer) {
var totalTime = 0,
initializer = initializer || function () {};
initializer();
var startTime = performance.now();
action();
var elapsed = performance.now() - startTime;
message('<span class="time"><span class="number">' +
Math.round(elapsed * 1000) + ' μs</span></span> ' + description);
}
function makeCanvas() {
var canvas = document.createElement('canvas'),
context = canvas.getContext('2d');
canvas.width = 100;
canvas.height = 100;
timeIt(function () {
context.fillStyle = '#a63d3d';
context.fillRect(10, 10, 80, 40); // Paint a small scene.
context.fillStyle = '#3b618c';
context.beginPath();
context.arc(60, 60, 25, 0, 2*Math.PI);
context.closePath();
context.fill();
}, '(millionths of a second) to draw original scene', function () {
context.clearRect(0, 0, canvas.width, canvas.height);
});
return canvas;
}
// copyCanvas returns a canvas containing the same image as the given canvas.
function copyCanvas(original) {
var copy;
timeIt(function () {
copy = original.cloneNode(); // Copy the canvas dimensions.
copy.getContext('2d').drawImage(original, 0, 0); // Copy the image.
}, 'to copy canvas with cloneNode and drawImage');
return copy;
}
// imageFromStorage extracts the image data from a canvas, stores the image data
// in a browser session, then retrieves the image data from the session and
// makes a new image element out of it. We measure the total time to retrieve
// the data and make the image.
function imageFromStorage(original) {
var image,
dataURI = original.toDataURL();
timeIt(function () {
image = document.createElement('img');
image.src = dataURI;
}, 'to make image from a dataURI');
return image;
}
function pageLoad() {
var target = document.getElementById('canvases'),
containers = {}, // We'll put the canvases inside divs.
names = ['original', 'cloneNode', 'drawImage', 'dataURI'];
for (var i = 0; i < names.length; ++i) {
var name = names[i], // Use the name as an ID and a visible header.
container = document.createElement('div'),
header = document.createElement('div');
container.className = 'container';
header.className = 'header';
header.innerHTML = container.id = name;
container.appendChild(header);
target.appendChild(container);
containers[name] = container; // The canvas container is ready.
}
var canvas = makeCanvas();
containers.original.appendChild(canvas); // Original canvas.
containers.cloneNode.appendChild(canvas.cloneNode()); // cloneNode
containers.drawImage.appendChild(copyCanvas(canvas)); // cloneNode + drawImage
containers.dataURI.appendChild(imageFromStorage(canvas)); // localStorage
}
pageLoad();
body {
font-family: sans-serif;
}
.header {
font-size: 18px;
}
.container {
margin: 10px;
display: inline-block;
}
canvas, img {
border: 1px solid #eee;
}
#message {
color: #666;
font-size: 16px;
line-height: 28px;
}
#message .time {
display: inline-block;
text-align: right;
width: 100px;
}
#message .number {
font-weight: bold;
padding: 1px 3px;
color: #222;
background: #efedd4;
}
<div id="canvases"></div>
<div id="message"></div>
If you call toDataURL to copy the image data into a string for use in other pages, don't put the string into a cookie. Cookies are meant to store small amounts of data. Instead, use the HTML5 Web Storage API to store the image data in the browser. Alternatively, if the image doesn't change between user sessions, you can render it to a PNG image on a server and use the Cache-Control header to encourage the browser to cache the image file for fast retrieval.
When it comes to the performance of client-side image rendering, it may be faster to draw the scene anew than to paint the stringified image data onto the canvas. Decoding the string and painting the pixels is a relatively expensive operation. To find out if it makes sense to redraw the scene on each page, you can time your drawing operations with performance.now, as demonstrated in the snippet.
I want to create a div overlay, set at 5% opacity, which creates a diagonal bounding box around the first line of text in this image. It seems to me that if I have the x,y coordinates for the four corners I should be able to create the overlay but I cannot figure out what the proper syntax would be to mark out the div. Am I wrong in thinking this can be done, and if not can someone point me at some methods for doing so?
Instead of using a canvas and javascript for simple cases you can use instead the transform property of css divs. For example with
#diag {
background-color: rgba(255, 0, 255, 0.25);
width: 700px;
height: 60px;
position: absolute;
left: 100px;
top: 160px;
transform: rotate(2deg) skew(30deg)
}
you can get a div positioned on the first line of the text in the image that can be styled easily with CSS for borders and that can be used directly for events (hover, clicks).
If you need to specify 4 corners however just a single transform is not enough in general and you have to draw your overlay in a canvas placed over the image. This works because a canvas is by default transparent and only what you explicitly draw will be visible leaving the image untouched in other areas. For example using:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
ctx.beginPath();
ctx.moveTo(166, 282);
ctx.lineTo(855, 296);
ctx.lineTo(897, 351);
ctx.lineTo(212, 349);
ctx.closePath();
ctx.fillStyle = "rgba(0, 255, 0, 0.25)";
ctx.strokeStyle = "#0F0";
ctx.lineWidth = 4;
ctx.fill();
ctx.stroke();
I've highlighted the third row in green (fill command) also adding a border (stroke command).
http://jsfiddle.net/hr9afs4z/1/