Adding a value to a hidden text box - javascript

I'm trying to add an image id value to a hidden text box whenever drawX is executed and and make it null when clear is executed. The way my program should work is that it should only draw an x on one image at a time. So whenever that image is selected it will send that image's id to the hidden text box that I named with an id of sendServ . The way I have it written right now, I don't think id is being passed to my drawX function in my OptionClick function. I'm not exactly sure how to get it to work. Maybe there is a simpler way by just using EventListener but I'm not sure. Any help would be appreciated, thank you.
Edit: I decided to add a snippet with a dummy array so it might be easier to help me.
let index = 0;
var hdnName = document.getElementById("sendServ");
const display = "table"; // or "grid" if horizontal, but this migh depend where you place the rest of the code, cause i added the style to the body
const x = 0;
const y = 0;
const images = {
height: 50,
width: 50,
url: [
"https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fen%2F8%2F84%2FAssociation_of_Gay_and_Lesbian_Psychiatrists_logo.jpg&f=1&nofb=1",
"https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fstatic01.nyt.com%2Fnewsgraphics%2F2016%2F07%2F14%2Fpluto-one-year%2Fassets%2Ficon-pluto.png&f=1&nofb=1",
"https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Ftse4.mm.bing.net%2Fth%3Fid%3DOIP.oFxADNN67dYP-ke5xg7HbQHaHG%26pid%3DApi&f=1",
"https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fmedia.glassdoor.com%2Fsqll%2F1065746%2Felevation-church-squarelogo-1453223965790.png&f=1&nofb=1"
],
id: [0, 1, 2, 3]
};
function createHTML() {
console.log("E: Execute & R: Request & I: Informative");
//loop to go true all images
document.body.style.display = display;
for (const image of images.url) {
//each image will correspond to a canvas element
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
//each canvas element will has is own properties (in this case all the same)
canvas.id = "option" + [index];
canvas.height = images.height;
canvas.width = images.width;
canvas.style.padding = "10px";
//function to get the corresponded image of that particular canvas element
drawImages(canvas);
//add an event listener for when a user click on the particular canvas
canvas.addEventListener("click", optionClick, false);
//all html part was handle we can append it to the body
document.body.appendChild(canvas);
index++;
}
}
function drawImages(canvas) {
//we need to use the getContext canvas function to draw anything inside the canvas element
const ctx = canvas.getContext("2d");
const background = new Image();
//This is needed because if the drawImage is called from a different place that the createHTML function
//index value will not be at 0 and it will for sure with an heigher id that the one expected
//so we are using regex to remove all letters from the canvas.id and get the number to use it later
index = canvas.id.replace(/\D/g, "");
//console.log('E: Drawing image ' + index + ' on canvas ' + canvas.id);
//get the image url using the index to get the corresponded image
background.src = images.url[index];
//no idea why but to place the image, we need to use the onload event
//https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
background.onload = function() {
ctx.drawImage(background, 0, 0, canvas.width, canvas.height);
};
}
function drawX(canvas, item) {
const ctx = canvas.getContext("2d");
// console.log("E: Placing X on canvas " + canvas.id);
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(images.width, images.height);
ctx.moveTo(images.height, 0);
ctx.lineTo(0, images.width);
ctx.closePath();
ctx.stroke();
hdnName.value = item;
}
function clear(canvas) {
//console.log("E: clearing canvas " + canvas.id);
canvas.getContext("2d").clearRect(0, 0, canvas.width, canvas.height);
drawImages(canvas);
hdnName.value = null;
}
function optionClick(e) {
log = true;
var index = 0;
const canvas = document.getElementsByTagName("canvas");
for (const option of canvas) {
if (log)
console.log("I: User clicked at option " + e.target.id + ":" + option.id);
log = false;
if (e.target.id === option.id) {
// console.log('R: Drawing request at canvas ' + option.id);
var item = images.id[index];
console.log(item);
drawX(option, item);
} else {
// console.log('R: Clearing request at canvas ' + option.id);
clear(option);
}
index++;
}
}
//We start by calling createHTML (that will handle with all HTML elements)
window.onload = createHTML;
canvas.suiteiCanvas {
height: auto;
width: auto;
max-height: 200px;
max-width: 150px;
margin-left: 100px;
margin-right: 100px;
border: 3px solid rgb(20, 11, 11);
}
#draw-btn {
font-size: 14px;
padding: 2px 16px 3px 16px;
margin-bottom: 8px;
}
img.new {
height: auto;
width: auto;
max-height: 200px;
max-width: 150px;
margin-left: 100px;
margin-right: 100px;
border: 3px solid rgb(20, 11, 11);
}
<body></body>
<input type="text" id="sendServ">

Related

EventListener and onClick - why do I have to click twice to trigger?

I am trying to extract base64 HTML image data after clicking a button which loads the base64 data from the server.
When testing locally, I have to click twice initially to get the expected results. And every subsequent click works as expected.
How can I shape this code so that it triggers on only one click?
<html>
<button id="t-load" type="button" data-board="g" data-tid="0" style="font-size: 11px; padding: 0px; width: 90px; box-sizing: border-box; margin: 0px 6px 0px 0px; vertical-align: middle; height: 18px;">Get Captcha</button>
<div id="t-fg" style="width: 100%; height: 100%; position: absolute; background-repeat: no-repeat; background-position: left top; background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPcAAABQAgMAAAC4+wuWAAAACVBMVEUL+Avu7u4AAACitRlfAAAAAXRSTlMAQObYZgAABsFJREFUWIWFmMGO2zYQhrlGt0B92i6iBZpTLwsUegq3aA71ySuIRKxTA9RFwqdYFHUPObXFsmh9Wi8iQ5qn7D8zpETZ3qyCJDQ1nzj8hxxxZKxewaaroS411/bc5dqh1Rlt+CO81+Z5/JBay4ST/neHvzX1yRlPE0z7rNtluHZSHI79dv3gCFqDpwDjhEKG0wRnxx3lOBXp1+ZoEsuiAS7P8m30lqhoaEBwj/Ynk9erpsJHfBM1Bkn0G1EYrSY/8svTNT2Y3KWNI72Ksa8c4zi5Kpjta5N39Y6eBM8GxHy253Axm+KtRY/w4kvC6UQ1nWbbTnB3oA1LosPH+Hk6UHsyfxhdd466yeiHPs2qyPB2SeXx4I7aKtgcx/rZ5XgTQ0ptrasiTHD+leNNsA1H/w3wyg6rB1PUJ2FANVzLTdazyfFeV3X5UZVz8V96LHRRJgGgjxc8HOFylQheMpPRzQXmsx1xbLi171bFMnjB12nuULSH/SHfaDCBjd+Pux8C43HGYHlOlefBMHqbi+QfjSmsy1Y+cI/laqDwQ4avbd0Br0uWaHWR8HtjLq19N9pBYE8rY0petONIvDslb8DaLswU12tV6FzfEXrnVNRmTAeB8aqQdg3GReWMSZ7YocW9sxZmuUxOVzfcgG8XnqWe4HhoHO09eo09wW+9yAffjRG87NRQktSKVcS1tG8TnquMbVRuO1uGiG/XK0p4E1y7UBkqYu/4UbrjbqNHi3lJW3rA6Hy3JNrt6FENZXTFsRAT/tZEzfn6mZGAP9ua786BHxT/Tn1UGeCjeIdHvRecHkSRBSNtGXg5KP6HU7xsEz6jnvPB/QR/7Ie7VAEXBu12rYbzln2OMmBH+tir+FKzG3ct0SoHfDBEqw/1gC+5ZS7ijnO8qa3cLY7wXcRlIatewF8rHpUHALV/lPn4Ar+EMc/gGKwY8Y0GPNgrHbIsfIYfIh6ewSUjwp0+4RRyvJfWTKQ9hzeKv6L6KjJheYq3U1wCCxmB8x362/pPshTMA20b0rsl84sRj2tlgntWBfOvOsXDFL+5+jzepAy41y5ErhrwQO4Lwfcv4t8nfJvjN2fxfwc8vg2cvdOuV9T5ES9vLj4/ekznTep6hT0/4taZxRC4iOP12I14SjS1dt0Af0j41jYv4hvFV4Pcv4TUREo2Vy+N3rB4mxEHM+Brw9rN6Hm8cSmN8oVYN9no9hS3U9yHzRRvB3zPqf0lfP/e3q4Tjs0nKVWe1HMOWUiKGtf8MY5dAocl85uLirqmn+BXjLfrHNfIarrAr0d4rHrh3dI3cfQl4/eMI13g3HmCu4jzzizvuWu+Ax4Ud3wivGft5sTheXZ0TisxJX/EkMVKH84p7J61m8vB5SzuEt6pw9RHwxzvhthIXj7C287vpG/WnOKQfoJnrwngfIrc/lT9J31fIQ77aHhpGX9k6csue0lNcQmctX/5T7C8ZfxuxAv7rYF2JSUcb2Ta+hyvBH/jW7+43PApEiKrSHxjleEIyHyD179P713ZMkTf4N+1HhsxOrLnIs4ysCMXhjK8JoiX40u9+4ErknVJfaV4sF5xs4g4H6fe8YzqT6KinudFYevC2r61ovwbNpzx8W0rE/4h4ngUibQpCJptZGfypmhaDRzfnjPeCn5N/2hOMrOvY6WiQVCcs61UjM1eVoFMk7gq3MiEv4zZ2K2Ku1GGy4TXIFrF7T0FeR3OJNS9GH4VcYhzJwdeCUiRcOt7LVf5fbkMtThEzqYjXlEqjnmtD3De8YHQ2AGv0rsCRWwVuESuOGuwqA3SbixfgRcujq4HzJSoKdZKsKt6JycZGZ16x4ZNiH1cF6nln4XienTV+tb6J0wAplwQFuoun4OdVjI4VR341WHTVet5s3FawDflU68VlNPTkh9PzCPehAnuIXsp0DWiHguw6gzuW9rRWLak0RtWmuP3RJ/6VHQ0UiuFEa6lVtuc4BxzhB5iP1F7l42WT5PnAzVe74cKj8sPeUmJY5wFficWv472zcRzlmNvxfm0ihRnq5pPIrccdyo4SDdncX73j58XvJTAjdXCs0vysng8wPjlQ0aT2tiTHcpRxje8LZa7VHW7Av/3MN1nEqXPMiXj2skeytwdBEJvwVWtC67/lWw85Y54mkUFwy53yNgP7DBXiTi0Y8V1NuJIFWHEP8QmTb5FAA8JR6AFXkV8/IKRfas5xiVqvSR3mKR7gqcf+acHorusko9lYK+CpCI3BMaXUaWhdMZTtyzw+M0rOn9QS9l29db2PeOa6lK34oeuzD4EpGNhwmUBIXA8eseebnOnGO/tBP8ftdeFnJm6qUIAAAAASUVORK5CYII=");"></div>
</html>
<script>
// turn string to image
function b64toImageData(datas) {
return new Promise(async function (resolve, reject) {
// We create an image to receive the Data URI
var img = document.createElement('img');
// When the event "onload" is triggered we can resize the image.
img.onload = function () {
// We create a canvas and get its context.
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// We set the dimensions at the wanted size.
canvas.width = img.width;
canvas.height = img.height;
// We resize the image with the canvas method drawImage();
ctx.drawImage(this, 0, 0, img.width, img.height);
// get image data
var imageDatas = ctx.getImageData(0, 0, img.width, img.height);
// This is the return of the Promise
resolve(imageDatas);
};
// add data to img
img.src = datas;
})
}
// event listener;
document.addEventListener("click", function () {
document.getElementById("t-load").onclick = async function () {
// get img
var fg = document.getElementById("t-fg").style.backgroundImage.slice(5, -2);
console.log(fg)
// get img data
var base_img = await b64toImageData(fg);
// log results
console.log(base_img.data);
}
});
</script>
Instead of the event handlers that you are using, you can delegate the click handler to the body and use still async.
function b64toImageData(datas) {
return new Promise(async function(resolve, reject) {
// We create an image to receive the Data URI
var img = document.createElement('img');
// When the event "onload" is triggered we can resize the image.
img.onload = function() {
// We create a canvas and get its context.
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// We set the dimensions at the wanted size.
canvas.width = img.width;
canvas.height = img.height;
// We resize the image with the canvas method drawImage();
ctx.drawImage(this, 0, 0, img.width, img.height);
// get image data
var imageDatas = ctx.getImageData(0, 0, img.width, img.height);
// This is the return of the Promise
resolve(imageDatas);
};
// add data to img
img.src = datas;
})
}
document.body.addEventListener("click", async(e) => {
let el = e.target;
if (el.id == "t-load") {
var fg = document.getElementById("t-fg").style.backgroundImage.slice(5, -2);
console.log(fg)
console.log("WAITING!! don't click again!")
var base_img = await b64toImageData(fg);
console.log(base_img.data);
}
});
<button id="t-load" type="button" data-board="g" data-tid="0" style="font-size: 11px; padding: 0px; width: 90px; box-sizing: border-box; margin: 0px 6px 0px 0px; vertical-align: middle; height: 18px;">Get Captcha</button>
<div id="t-fg" style="width: 100%; height: 100%; position: absolute; background-repeat: no-repeat; background-position: left top; background-image: url("data:image/png;base64,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");"></div>

How to change font color based on background image with javascript?

I want to change the font color based on the color of the image. The font color must be black or white, but it should change proportionally to the color of the image as it does in the code below.
So, I have this js function that if you move the mouse it changes the background color and consequently also the text (in the snippet move the pointer under the background). Obviously the part where you move the mouse to change color doesn't interest me, it's just a test. Is there a way to apply the function to my personal div?
In simple terms I want the color of the text to change based on my background and not if you move the mouse. Sorry for the poor explanation, I'm relatively new here, trying to learn how to exercise this function. Currently I don't know how to do it, I appreciate any answer, thanks.
function getTextColor(rgba){
rgba = rgba.match(/\d+/g);
if((rgba[0]*0.299)+(rgba[1]*0.587)+(rgba[2]*0.114)>186) {
return 'black';
} else {
return 'white';
}
}
var div = document.createElement('div');
div.style.height = '100vh';
document.body.appendChild(div);
div.addEventListener('mousemove', (event)=>{
var x = event.clientX;
var y = event.clientY;
div.textContent = `${x} , ${y}`;
div.style.backgroundColor = `rgba(${x},${y},100,100)`;
div.style.color = getTextColor(div.style.backgroundColor);
});
.background {
height: 100vh;
background: url("https://i.pinimg.com/originals/bc/a3/2d/bca32d5cdfabeaeb4faeb12bff160524.jpg");
}
<div class="background">Background example</div>
Explanation
Here is an example showing how to get the average using a canvas rendered to a 1x1-px scale. From there we set the header background and then apply a light or dark class based on the luminance of the aforementioned average.
Notes
Note 1:
There is some extra code to allow for easier visualization, this is commented and can be removed.
Note 2: The below code is not guaranteed to run on all browsers. Please see this thread for more specifics.
Note 3: This code only works with local images and CORS-enabled image URLs due to it's reliance on canvas.drawImage. Please see this for more information.
Example 1:
this example shows the functionality. It uses two image URLs in the JS to toggle between.
/*
THE TOGGLE ACTION
*/
/* The urls for toggling between */
const urlLight = 'https://images.unsplash.com/photo-1484503793037-5c9644d6a80a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=930&q=80';
const urlDark = 'https://images.unsplash.com/photo-1517999144091-3d9dca6d1e43?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=627&q=80';
let currentUrl = urlLight;
const toggle = document.getElementById('toggle');
toggle.addEventListener("click", function() {
if (currentUrl === urlLight) {
currentUrl = urlDark;
} else {
currentUrl = urlLight;
}
getImageBrightness(currentUrl, function(brightness) {
const header = document.getElementById("header");
header.style.backgroundImage = `url(${currentUrl}`;
header.classList.remove("dark");
header.classList.remove("light");
console.log(brightness);
if (brightness > 225 / 2) {
header.classList.toggle("dark");
} else {
header.classList.toggle("light");
}
});
});
/*
Important get brightness code
*/
function getImageBrightness(imageSrc, callback) {
var img = document.createElement("img");
img.src = imageSrc;
img.style.display = "none";
img.crossOrigin = "anonymous";
document.body.appendChild(img);
var colorSum = 0;
img.onload = function() {
// create canvas
var canvas = document.createElement("canvas");
canvas.width = this.width;
canvas.height = this.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(this, 0, 0);
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = imageData.data;
var r, g, b, avg;
for (var x = 0, len = data.length; x < len; x += 4) {
r = data[x];
g = data[x + 1];
b = data[x + 2];
avg = Math.floor((r + g + b) / 3);
colorSum += avg;
}
var brightness = Math.floor(colorSum / (this.width * this.height));
callback(brightness);
}
}
getImageBrightness(currentUrl, function(brightness) {
const header = document.getElementById("header");
header.style.backgroundImage = `url(${currentUrl}`;
header.classList.remove("dark");
header.classList.remove("light");
if (brightness > 225 / 2) {
header.classList.toggle("dark");
} else {
header.classList.toggle("light");
}
});
.dark {
color: black;
}
.light {
color: white;
}
* {
text-align: center;
}
#header {
padding: 2rem 0;
font-size: 4rem;
background-size: cover;
margin-bottom: 1rem;
background-position: center center;
}
<div id="header">
Header
</div>
<button id='toggle'>
Toggle Background
</button>
Example 2:
This example directly answers the question by retrieving the background image of the current div and running the function using said image.
/*
Important get brightness code
*/
function getImageBrightness(imageSrc, callback) {
var img = document.createElement("img");
img.src = imageSrc;
img.style.display = "none";
img.crossOrigin = "anonymous";
document.body.appendChild(img);
var colorSum = 0;
img.onload = function() {
// create canvas
var canvas = document.createElement("canvas");
canvas.width = this.width;
canvas.height = this.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(this, 0, 0);
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = imageData.data;
var r, g, b, avg;
for (var x = 0, len = data.length; x < len; x += 4) {
r = data[x];
g = data[x + 1];
b = data[x + 2];
avg = Math.floor((r + g + b) / 3);
colorSum += avg;
}
var brightness = Math.floor(colorSum / (this.width * this.height));
callback(brightness);
}
}
/* The element we wish to work on */
const header = document.getElementById("header");
/* To get the background image. Get style and then background image with a slice to remove quotes */
var imageElement = document.getElementById('header'),
style = imageElement.currentStyle || window.getComputedStyle(imageElement, false),
backgroundImage = style.backgroundImage.slice(4, -1).replace(/"/g, "");
getImageBrightness(backgroundImage, function(brightness) {
/* Remove dark and light classes from the element */
header.classList.remove("dark");
header.classList.remove("light");
/* check brightness and apply correct style */
/* Please modify the below brightness comparison statement to adjust to your needs */
if (brightness > 225 / 2) {
header.classList.toggle("dark");
} else {
header.classList.toggle("light");
}
});
.dark {
color: black;
}
.light {
color: white;
}
* {
text-align: center;
}
#header {
padding: 2rem 0;
font-size: 4rem;
background-size: cover;
margin-bottom: 1rem;
background-position: center center;
background-image: url('https://images.unsplash.com/photo-1530128118208-89f6ce02b37b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1740&q=80')
}
<div id="header">
<h1>
Header
</h1>
</div>

Optimise HTML5 canvas

Why does html5 canvas become very slow when I draw 2000 images and more ?
How can I optimise it?
Here's a demo that I've made, disable the "safe mode" by left clicking on the canvas and start moving your mouse until you get ~2000 images drawn
var img = new Image()
img.src = "http://i.imgur.com/oVOibrL.png";
img.onload = Draw;
var canvas = $("canvas")[0];
var ctx = canvas.getContext("2d")
var cnv = $("canvas");
var draw = [];
$("canvas").mousemove(add)
function add(event) {
draw.push({x: event.clientX, y: event.clientY})
}
canvas.width = cnv.width();
canvas.height = cnv.height();
var safe = true;
cnv.contextmenu(function(e) { e.preventDefault() })
cnv.mousedown(function(event) {
if(event.which == 1) safe = !safe;
if(event.which == 3) draw = []
});
function Draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
requestAnimationFrame(Draw);
for(var i of draw) {
ctx.drawImage(img, i.x, i.y)
}
if(safe && draw.length > 300) draw = []
ctx.fillText("Images count: "+ draw.length,10, 50);
ctx.fillText("Left click to toggle the 300 images limit",10, 70);
ctx.fillText("Right click to clear canvas",10, 90);
}
Draw();
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
position: absolute;
}
canvas {
position: absolute;
width: 100%;
height: 100%;
z-index: 99999999999;
cursor: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas></canvas>
Codepen: http://codepen.io/anon/pen/PpeNme
The simple way with the actual code :
Don't redraw all your images at this rate.
Since in your example, the images are static, you actually don't need to redraw everything every frame : Just draw the latest ones.
Also, if you've got other drawings occurring (e.g your texts), you may want to use an offscreen canvas for only the images, that you'll redraw on the onscreen canvas + other drawings.
var img = new Image()
img.src = "http://i.imgur.com/oVOibrL.png";
img.onload = Draw;
var canvas = $("canvas")[0];
var ctx = canvas.getContext("2d")
var cnv = $("canvas");
var draw = [];
$("canvas").mousemove(add)
function add(event) {
draw.push({
x: event.clientX,
y: event.clientY
})
}
canvas.width = cnv.width();
canvas.height = cnv.height();
// create an offscreen clone of our canvas for the images
var imgCan = canvas.cloneNode();
var imgCtx = imgCan.getContext('2d');
var drawn = 0; // a counter to know how much image we've to draw
var safe = true;
cnv.contextmenu(function(e) {
e.preventDefault()
})
cnv.mousedown(function(event) {
if (event.which == 1) safe = !safe;
if (event.which == 3) draw = []
});
function Draw() {
// clear the visible canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
requestAnimationFrame(Draw);
if (draw.length) { // onmy if we've got some objects to draw
for (drawn; drawn < draw.length; drawn++) { // only the latest ones
let i = draw[drawn];
// draw it on the offscreen canvas
imgCtx.drawImage(img, i.x, i.y)
}
}
// should not be needed anymore but...
if (safe && draw.length > 300) {
draw = [];
drawn = 0; // reset our counter
// clear the offscren canvas
imgCtx.clearRect(0, 0, canvas.width, canvas.height);
}
// draw the offscreen canvas on the visible one
ctx.drawImage(imgCan, 0, 0);
// do the other drawings
ctx.fillText("Images count: " + draw.length, 10, 50);
ctx.fillText("Left click to toggle the 300 images limit", 10, 70);
ctx.fillText("Right click to clear canvas", 10, 90);
}
Draw();
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
position: absolute;
}
canvas {
position: absolute;
width: 100%;
height: 100%;
z-index: 99999999999;
cursor: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas></canvas>
Now, if you need these images to be dynamic (i.e move at every frame), you may consider using imageDatas.
You can see this original post by user #Loktar, which explains how to parse your drawn image imageData, and then redraw it pixel per pixel on the visible canvas' imageData. You can also see this follow-up Q/A, which provides a color implementation of Loktar's idea.
On small images, this actually improves drastically the performances, but it has the huge inconvenient to not support alpha channel multiplication. You will only have fully transparent and fully opaque pixels. An other cons is that it may be harder to implement, but this is just your problem ;-)

Edited: Switch between viewable ares of the HTML <canvas> preferably without page load

EDIT (original post at bottom below code)**
Fiddle: https://jsfiddle.net/Tron4949/oezuz1g9/9/
(fiddle example will not have a background, it's unnecessary to demonstrate the issue, the console log shows more detail about which view you are on, what was just saved, etc.)
Here is my attempt based on the suggestions from below. It has changed forms many times - currently when the user loads an image, and positions it where they want, then toggles to another view, the uploaded image carries over to that other view (which is undesired - each view should have their own specific layout).
I added 'front', 'side', and 'back' buttons to correspond to different canvas views. When you toggle back and forth - I track the canvas view ("front", "side", or "back") in session storage, defaulting to "front" at the very beginning. Depending on which button is pressed (for which view), it redefines the string in sessionStorage so that we know what the previous view was supposed to be.
It then saves the content of the canvas to storage as a dataURL according to the view it just came from. (*It then clears the canvas and accesses the dataURL for the correct position of the background picture that the user chose earlier on - this is not displayed in the fiddle).
From this point is where I keep having to re-configure things, and I can not find a solution to make it run properly, that is, to make it run without images carrying over.
As is, the image that the user uploaded not only carries over to the other views when you toggle between them, but anything I do with that image, or a newly uploaded one, continues to carry over as you toggle back and forth between views, to the point where each of the three uploaded images and positions are all the same(unexpected/undesired).
A stripped down version - The three click handlers for 'views' are basically the same:
HTML
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.min.js"></script>
<canvas id="c" width="740" height="500" style="border-left: 3px solid black; border-top: 3px solid black; border-radius: 10px;"></canvas>
<button class="btn" id="customer-chosen-background-image">Set A Background Image</button>
<input class="btn" type="file" id="files" name="files[]"/>
<button class="btn" id="sessionInfoClear">Clear My Session</button>
<hr>
<button class="btn view-btn" id="front-hat-view-button">Front</button>
<button class="btn view-btn" id="side-hat-view-button">Side</button>
<button class="btn view-btn" id="back-hat-view-button">Back</button>
<canvas id="d" width="740" height="250" style="border-left: 3px solid black; border-top: 3px solid black; border-radius: 10px;"></canvas>
JS
$(document).ready(function() {
var canvas = new fabric.Canvas('c');
var canvas2 = new fabric.Canvas('d');
var scopeTesterOne = "testing scope";//SOMETHING I DO OCCASIONALLY
document.getElementById('files').addEventListener("change", function (e) {
var file = e.target.files[0];
var reader = new FileReader();
reader.onload = function (f) {
var data = f.target.result;
fabric.Image.fromURL(data, function (img) {
var oImg = img.set({left: 350, top: 300, angle: 00,width:100, height:100}).scale(0.9);
canvas.add(oImg).renderAll();
var a = canvas.setActiveObject(oImg);
});
};
reader.readAsDataURL(file);
$('#files').css({'background-color': 'white', 'color': 'black', 'border': '2px solid black'});
});
$('#customer-chosen-background-image').click(function() {
var backgroundImageFilePath = 'http://store-1p9yfoynqe.mybigcommerce.com/product_images/Viewsforfiddle.png';
// var backgroundURL = "url(" + backgroundImageFilePath + ")"
canvas.setBackgroundImage(backgroundImageFilePath, canvas.renderAll.bind(canvas), {
top: 0,
left: 50,
scaleY: .8,
scaleX: .8
});
});
var whatIsTheHatViewOnCanvas = function() {//INITIALLY THIS IS HOW WE TELL/TRACK/SET WHAT THE PREVIOUS VIEW WAS
var doesTrackingDataExist = sessionStorage.getItem('viewForHatCanvas');
if (doesTrackingDataExist) {
console.log("traker has previous view data");
} else {
console.log("traker has no previous view data");
sessionStorage.setItem('viewForHatCanvas', "front");
var consoleTestttt = sessionStorage.getItem('viewForHatCanvas');
console.log(consoleTestttt);
}
};
whatIsTheHatViewOnCanvas();
function loadCanvas(dataURL) { //THIS IS FOR THE MAIN CANVAS
var canvas = document.getElementById('c');
var context = canvas.getContext('2d');
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(this, 0, 0);
};
imageObj.src = dataURL;
}
function loadPreviewCanvasFront(dataURL) {//IMAGES FOR THE 'PREVIEW' CANVAS
var canvas2 = document.getElementById('d');
var context = canvas2.getContext('2d');
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(this, 0, 0, 185, 125);
};
imageObj.src = dataURL;
}
function loadPreviewCanvasSide(dataURL) {//IMAGES FOR THE 'PREVIEW' CANVAS
var canvas2 = document.getElementById('d');
var context = canvas2.getContext('2d');
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(this, 185, 0, 185, 125);
};
imageObj.src = dataURL;
}
function loadPreviewCanvasBack(dataURL) {//IMAGES FOR THE 'PREVIEW' CANVAS
var canvas2 = document.getElementById('d');
var context = canvas2.getContext('2d');
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(this, 370, 0, 185, 125);
};
imageObj.src = dataURL;
}
$('#front-hat-view-button').click(function() {
console.log(scopeTesterOne);
var context = canvas.getContext('2d');
var previousHatView = sessionStorage.getItem('viewForHatCanvas');
sessionStorage.setItem('viewForHatCanvas', "front");
console.log("from " + previousHatView + " to front");
canvas.deactivateAll().renderAll();
var savingCanvasContentF = canvas.toDataURL("image");
if (previousHatView === "side") {
sessionStorage.setItem('storedSideHatCreation', savingCanvasContentF);
console.log("from " + previousHatView + " to front - STORED AS SIDE VIEW");
}
if (previousHatView === "back") {
sessionStorage.setItem('storedBackHatCreation', savingCanvasContentF);
console.log("from " + previousHatView + " to back - STORED AS BACK VIEW");
}
// canvas.clear();
context.clearRect(0, 0, canvas.width, canvas.height);
var imgToLoadOnFrontView = sessionStorage.getItem('storedFrontHatCreation');
//var backgroundImageFilePath =
//var backgroundURL = "url(" + backgroundImageFilePath + ")"
//canvas.setBackgroundImage(backgroundImageFilePath, canvas.renderAll.bind(canvas), {
//top: 0,
//left: 50,
//scaleY: .8,
//scaleX: .8
//});
if (imgToLoadOnFrontView != null) {
context.clearRect(0, 0, canvas.width, canvas.height);
console.log("says there is an image to load on front");
loadCanvas(imgToLoadOnFrontView);
loadPreviewCanvasFront(imgToLoadOnFrontView);
}
});
$('#side-hat-view-button').click(function() {
var context = canvas.getContext('2d');
var previousHatView = sessionStorage.getItem('viewForHatCanvas');
var currentHatView = "side";
sessionStorage.setItem('viewForHatCanvas', "side");
canvas.deactivateAll().renderAll();
var savingCanvasContentS = canvas.toDataURL("image");
if (previousHatView === "front") {
sessionStorage.setItem('storedFrontHatCreation', savingCanvasContentS);
console.log("from " + previousHatView + " to side - STORED AS FRONT VIEW");
}
if (previousHatView === "back") {
sessionStorage.setItem('storedBackHatCreation', savingCanvasContentS);
console.log("from " + previousHatView + " to side - STORED AS BACK VIEW");
}
// canvas.clear();
context.clearRect(0, 0, canvas.width, canvas.height);
var imgToLoadOnSideView = sessionStorage.getItem('storedSideHatCreation');
// var backgroundImageFilePath = sessionStorage.getItem('hatImageLocation');
// var backgroundURL = "url(" + backgroundImageFilePath + ")"
// canvas.setBackgroundImage(backgroundImageFilePath, canvas.renderAll.bind(canvas), {
// top: -560,
// left: 50,
// scaleY: .8,
// scaleX: .8
// });
if (imgToLoadOnSideView != null) {
context.clearRect(0, 0, canvas.width, canvas.height);
console.log("says there is an image to load on side");
loadCanvas(imgToLoadOnSideView);
loadPreviewCanvasSide(imgToLoadOnSideView);
}
});
$('#back-hat-view-button').click(function() {
var context = canvas.getContext('2d');
var previousHatView = sessionStorage.getItem('viewForHatCanvas');
sessionStorage.setItem('viewForHatCanvas', "back");
console.log("from " + previousHatView + " to back");
canvas.deactivateAll().renderAll();
var savingCanvasContentB = canvas.toDataURL("image");
if (previousHatView === "front") {
sessionStorage.setItem('storedFrontHatCreation', savingCanvasContentB);
console.log("from " + previousHatView + " to back - STORED AS FRONT VIEW");
}
if (previousHatView === "side") {
sessionStorage.setItem('storedSideHatCreation', savingCanvasContentB);
console.log("from " + previousHatView + " to back - STORED AS SIDE VIEW");
}
// canvas.clear();
context.clearRect(0, 0, canvas.width, canvas.height);
var imgToLoadOnBackView = sessionStorage.getItem('storedBackHatCreation');
//var backgroundImageFilePath = sessionStorage.getItem('hatImageLocation');
//var backgroundURL = "url(" + backgroundImageFilePath + ")"
//canvas.setBackgroundImage(backgroundImageFilePath, canvas.renderAll.bind(canvas), {
// top: -1065,
// left: 50,
// scaleY: .8,
// scaleX: .8
//});
if (imgToLoadOnBackView != null) {
context.clearRect(0, 0, canvas.width, canvas.height);
console.log("says there is an image to load on back");
loadCanvas(imgToLoadOnBackView);
loadPreviewCanvasBack(imgToLoadOnBackView);
}
});
$('#sessionInfoClear').click(function() {
sessionStorage.clear();
location.reload();
});
});
ORIGINAL POST (below):
I have a canvas that users can interact with (to create a hat). It has one background image that has three 'sections' on it for front side and back. As of now, a user is able to upload their own images and manipulate them on the canvas to make their own designs - I am using Fabric.js for image manipulation.
My Goal: Although the background image is one large image with three 'sections' on it, I only want the user to be able to see one of those sections at a time, and then be able to switch back and forth between any of the three of them while they make their picture/creation (without a page load - page load removes the uploaded images).
My thoughts: Change the canvas's viewable area on a click - I can not figure out how to make a specific viewable area but I feel like this is the best option I can think of. I just need to be able to figure out how to have a 'viewing window' that is smaller than the canvas, and then I could use thumbnails or buttons to change whatever parameters I need to change so that it appears to be a different canvas (without a reload - it will remove the images).
Is this realistic and or possible? Is there a particular/better way that I have not considered? I really would like to keep the app (mostly) in-tact, but if I absolutely have to change everything, then I will, but I'm hoping someone knows how to do what I am proposing. Thanks so much for your time.
I think there is an easier solution then what you are trying to do. if you have the reference to the fabric instance you can get the whole object state out of it with: var currentCanvasData = canvas.toObject(). You will get a nice javascript object that you can pass in later to recreate the canvas. canvas.clear() will remove everything from the canvas and canvas.loadFromJSON(currentCanvasData) will load it all back up. Make sure to call canvas.renderAll() after loadFromJSON.
this will make it easy to switch bewteen 3 different work spaces for you and the user.
var canvas = new fabric.Canvas('c');
var currentView = 1;
var view1Save = {};
var view2Save = {};
var view1 = document.querySelector('.one');
var view2 = document.querySelector('.two');
view1.onclick = (function() {
if (currentView === 2) {
view2Save = canvas.toObject();
canvas.clear();
canvas.loadFromJSON(view1Save, canvas.renderAll.bind(canvas));
currentView = 1;
view1.disabled = true
view2.disabled = false
}
});
view2.onclick = (function() {
if (currentView === 1) {
view1Save = canvas.toObject();
canvas.clear();
canvas.loadFromJSON(view2Save, canvas.renderAll.bind(canvas));
currentView = 2;
view1.disabled = false
view2.disabled = true
}
});
var addRect = document.querySelector('.rect');
addRect.onclick = (function() {
canvas.add(new fabric.Rect({
left: 100,
top: 100,
width: 50,
height: 50,
fill: 'red'
}));
});
var addImg = document.querySelector('.image');
addImg.onclick = (function() {
var imgElement = new Image();
imgElement.src = 'http://cdn.sstatic.net/Sites/stackoverflow/img/sprites.png';
imgElement.onload = (function() {
canvas.add(new fabric.Image(imgElement, {
left: 100,
top: 100,
angle: 30,
opacity: 0.85
}));
});
});
canvas { border: 1px solid #ccc; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.4/fabric.min.js"></script>
different views:
<button class="one" disabled>view 1</button>
<button class="two" >view 2</button>
<br />
<button class="rect">add rect</button>
<button class="image">add image</button>
<canvas id="c" width="600" height="600"></canvas>

Drawing repeatedly updating images to the canvas (Safari blanks out image during loading)

I'm drawing an image onto the background of a canvas. This image refreshes every second, but the canvas needs to be redrawn more often than that, every 0.5 seconds. The problem I seem to be running into is that Safari flickers when updating the canvas because it appears to lose the image data from the bgImg variable. Firefox works as expected and no flicker occurs.
Does anyone have any clue as to how to avoid this? Am I missing something really obvious?
To test this code, simply paste the entire code below into an html file and load with your browser. What it should do is:
create a canvas
load an image every second
redraw the canvas every 500ms, using the image as a background.
Here's the code:
<html>
<head>
<script type="text/javascript">
var bgImg = new Image();
var firstload = false;
var cv;
var ctx;
var updateBackground = function() {
bgImg.onload = function() {
firstload = true;
console.log("image loaded. Dimensions are " + bgImg.width + "x" + bgImg.height);
}
bgImg.src = "http://mopra-ops.atnf.csiro.au/TOAD/temp.php?" + Math.random() * 10000000;
setTimeout(updateBackground, 1000);
}
var initGraphics = function() {
cv = document.getElementById("canvas");
ctx = cv.getContext("2d");
cv.width = cv.clientWidth;
cv.height = cv.clientHeight;
cv.top = 0;
cv.left = 0;
}
var drawStuff = function() {
console.log("Draw called. firstload = " + firstload );
ctx.clearRect(0, 0, cv.width, cv.height);
if ( firstload == true) {
console.log("Drawing image. Dimensions are " + bgImg.width + "x" + bgImg.height);
try {
ctx.drawImage(bgImg, 0, 0);
} catch(err) {
console.log('Oops! Something bad happened: ' + err);
}
}
setTimeout(drawStuff, 500);
}
window.onload = function() {
initGraphics();
setTimeout(updateBackground, 1000);
setTimeout(drawStuff, 500);
}
</script>
<style>
body {
background: #000000;
font-family: Verdana, Arial, sans-serif;
font-size: 10px;
color: #cccccc;
}
.maindisplay {
position:absolute;
top: 3%;
left: 1%;
height: 96%;
width: 96%;
text-align: left;
border: 1px solid #cccccc;
}
</style>
</head>
<body>
<div id="myspan" style="width: 50%">
<canvas id="canvas" class="maindisplay"></canvas>
</div>
</body>
</html>
Your problem is that you are drawing bgImg while it's loading. Webkit does not cache the old image data while loading a new image; it wipes out the image as soon as a new .src is set.
You can fix this by using two images, using the last-valid-loaded one for drawing while the next one is loading.
I've put an example of this online here: http://jsfiddle.net/3XW8q/2/
Simplified for Stack Overflow posterity:
var imgs=[new Image,new Image], validImage, imgIndex=0;
function initGraphics() {
// Whenever an image loads, record it as the latest-valid image for drawing
imgs[0].onload = imgs[1].onload = function(){ validImage = this; };
}
function loadNewImage(){
// When it's time to load a new image, pick one you didn't last use
var nextImage = imgs[imgIndex++ % imgs.length];
nextImage.src = "..."+Math.random();
setTimeout(loadNewImage, 2000);
}
function updateCanvas(){
if (validImage){ // Wait for the first valid image to load
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(validImage, 0, 0);
}
setTimeout(updateCanvas, 500);
}

Categories