How to change origin of html5 canvas? - html

I couldn't find answers for html5 canvas. Can I change the coordinates of the origin, so that my grafics will move altogether to the right for 15px (just an example). Given the following html and css?
<canvas id="g" width="auto" height="auto">Your browser doesnt support canvas tag</canvas>
css:
canvas, section {
display: block;
}
#g {
position: absolute;
width:100%;
height:100%;
overflow:hidden;
}

Sounds like you need a translate transform. Depending on what else you're doing with the context, you may also need to call save() and restore().
var ctx = document.getElementById('myCanvas').getContext('2d');
ctx.save();
ctx.translate(15, 0);
// ... do stuff with the transformed origin
ctx.restore();
// ... do stuff with the canvas restored to its original state (if necessary)

Related

How to add a tint to a background image with transparency in CSS

I have some content on a page that serves as a global background.
I put a sprite (div with background-image: url(...), changing frames by modifying background-position) on top of that using position: absolute. The sprite is a PNG with alpha-channel.
Now I'm trying to add some tint to that image (greenish or blueish or other).
I've studied the similar questions and apparently the only possible solutions are:
Create a div on top of the sprite with the desired color as its background-color, desired tint strength as opacity and the original sprite image as mask-image (and setting the mask-type: alpha). While it should work on paper, it doesn't in practice - this new div is just invisible :(
Use mix-blend-mode for the overlaying colored div and specify something like multiply or overlay. It produces perfect results as long as the global background is black. If it's something else - it gets included in the calculations and the overlay div modifies it as well, producing a tinted rectangle instead of tinted sprite...
Use SVG filter as described in an answer here: https://stackoverflow.com/a/30949302/306470 .
I didn't try this one yet, but it feels unnecessary complicated for this task. I'm concerned about the performance here too, will it slow down things a lot if there will be multiple tinted sprites on the screen at the same time? If anyone had experience with it - please comment here :)
Prepare a tinted version of the sprite using an invisible canvas. Sounds even more complicated, has a disadvantage of having to spend time to prepare the tinted version of the sprite, but should work as fast as the original sprite once it's prepared? Implementation should be pretty complicated though. Pure CSS solution would be much better...
Am I missing something? Are there any other options? Or should I go with #3 or #4?
Here is a working example of the outlined comment I left, hope it helps. I use a created div element to overlay on top of the image. Get the image elements position using boundingClientRect and this.width/this.height inside a for loop looping over the image elements. Set the overlay elements position to that of the image element being looped over and randomize a color using function with rgb setting alpha to 0.5.
let fgrp = document.getElementById("group");
let images = document.querySelectorAll(".imgs");
//function to randomize the RGB overlay color
function random() {
var o = Math.round,
r = Math.random,
s = 255;
return o(r() * s);
}
//function to randomize a margin for each image to show the overlay will snap to the image
function randomWidth() {
var n = Math.round,
ran = Math.random,
max = 400;
return n(ran() * max);
}
// loop through the img elements and create an overlay div element for each img element
for (let i = 0; i < images.length; i++) {
// load the images and get their wisth and height
images[i].onload = function() {
let width = this.width;
let height = this.height;
this.style.marginLeft = randomWidth() + "px";
// create the overlay element
let overlay = document.createElement("DIV");
// append the overlay element
fgrp.append(overlay);
// get the image elements top, left positions using `getBoundingClientRect()`
let rect = this.getBoundingClientRect();
// set the css for the overlay using the images height, width, left and top positions
// set position to absolute incase scrolling page, zindex to 2
overlay.style.cssText = "width: " + this.offsetWidth + "px; height: " + this.offsetHeight + "px; background-color: rgba(" + random() + ", " + random() + ", " + random() + ", 0.5); left: " + rect.left + "px; top: " + rect.top + "px; position: absolute; display: block; z-index: 2; cursor pointer;";
}
}
img {
margin: 50px 0;
display: block;
}
<div id="group">
<image src="https://artbreeder.b-cdn.net/imgs/275c7c05efca3a40e3178208.jpeg?width=256" class="imgs"></image>
<image src="https://artbreeder.b-cdn.net/imgs/275c7c05efca3a40e3178208.jpeg?width=256" class="imgs"></image>
<image src="https://artbreeder.b-cdn.net/imgs/275c7c05efca3a40e3178208.jpeg?width=256" class="imgs"></image>
</div>
Following the guide below, it is possible to at a colorful tint over a div/image using only CSS, like so:
<html>
<head>
<style>
.hero-image {
position: relative;
width: 100%;
}
.hero-image:after {
position: absolute;
height: 100%;
width: 100%;
background-color: rgba(0, 0, 0, .5);
top: 0;
left: 0;
display: block;
content: "";
}
</style>
</head>
<body>
<div class="hero-image">
<img src="https://cdn.jpegmini.com/user/images/slider_puffin_before_mobile.jpg" alt="after tint" />
</div>
<div>
<img src="https://cdn.jpegmini.com/user/images/slider_puffin_before_mobile.jpg" alt="before tint" />
</div>
</body>
</html>
Since you are trying to place it on top of an absolute position image, as the guide says, add a z-index of 1 (for example) to the :after chunk.
Edit: It might need some tweaking on the width's percentage!
Source: https://ideabasekent.com/wiki/adding-image-overlay-tint-using-css

Animate element when new element is loaded

I don't think I can explain this very well in words, so here's a gif:
The p is a <p> tag that appears when it's display is changed to block in js (by default it's none).
When this happens, h1 shifts a bit upward. (here it's only one line, it's usually more)
How can I go about animating h1's movement upward?
You can harness display: table and display: table-cell properties to emulate a gravity to the bottom using vertical-align: bottom.
Then with a little bit of jQuery goodness, you should be able to trigger CSS animations on specific events, such as the loading of a new element.
Check out this implementation on JSFiddle
In the snippet, hObj is the h1 tag you wish to move and pObj is the paragraph tag you want to insert later. You can use css transitions to animate objects.
var hObj = document.getElementById("hObj");
var pObj = document.getElementById("pObj");
function move(){
hObj.style.top = "60px";
hObj.addEventListener("transitionend", function(){ pObj.style.visibility = "visible"; });
}
<h1 style="position:absolute; left:100px; top:100px; transition:top 0.5s linear; cursor:pointer; " id="hObj" onclick="move()">h1</h1>
<p style="position:absolute; left:100px; top:100px; visibility:hidden" id="pObj">p</p>

How to cache SVG icons on an external CDN while avoiding FOMI?

I know how to get SVG icons loading on my website, but what I can't figure out is how to satisfy all the following constraints:
Ability to use SVG icons in CSS
No flash of missing icons (FOMI)
Minimal initial page size
Cached SVGs
Ability to use a CDN
Must be able to use fill: currentColor to make the icon match the current text color, just like icon-fonts
Bonus: Pixel-align the SVGs so they always look sharp
1,2,3 and 4 can be satisfied by using an external sprite map like so:
<svg viewBox="0 0 100 100">
<use xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/assets/sprite-4faa5ef477.svg#icon-asterisk-50af6"></use>
</svg>
But we can't use a CDN until browsers fix the CORS issue.
We can patch in support for external domains, but I'm pretty sure this won't work for CSS because it only watches the DOM (sorry, haven't tested yet), and also it causes your browser to make a whole bunch of failed requests to a file it can't fetch (one for each icon on the page).
We can use a CDN if instead we either inline the entire SVG (increased page size, no caching) or we AJAX it in (causes FOMI).
So, are there any solutions that satisfy all 5 7 constraints?
Basically I want SVGs to be just as convenient as icon-fonts or there's no point switching over. SVGs support multiple colors and are more accessible but I can't get them to look as good, or load as efficiently.
The closest I could get is loading an SVG in an image element and then using it like an "old-fashioned" image sprite. This, as far as I can tell, satisfies all of your constraints. The only disadvantage I can think of is that you lose the ability to modify specific parts of the SVG using CSS. This is however not one of your constraints (correct me if I'm wrong) and it is still possible to modify all of the icon, as you can see in my demo. I created a fiddle and for completeness also include a code snippet.
To emulate a CDN, I created an SVG file and uploaded it to some image hosting service. My apologies to future readers if this service is now down. The SVG file simply has all icons next to each other in it (I created a black square, circle and triangle for now). The difference with SVG sprite maps is thus that the icons are in the SVG itself, not in the defs. It should be quite easy to combine multiple SVGs in a single one, I have not looked for tools that would automate this process.
.icon {
display: inline-block;
vertical-align: top;
width: 30px; /* can be anything */
height: 30px;
background-image: url('http://imgh.us/icons_36.svg');
border: 1px solid #000; /* just to see where the icon is */
}
/* sizes */
.icon.large {
width: 50px;
height: 50px;
background-size: 150px auto;
}
/* icons */
.icon.circle { background-position: -30px 0; }
.icon.large.circle { background-position: -50px 0; }
.icon.triangle { background-position: -60px 0; }
.icon.large.triangle { background-position: -100px 0; }
/* styles */
.icon.info {
/* based on http://stackoverflow.com/a/25524145/962603,
* but you can of course also use an SVG filter (heh) */
filter: invert(100%) sepia(100%) saturate(50000%) hue-rotate(90deg) brightness(70%);
}
.icon.highlight {
/* based on http://stackoverflow.com/a/25524145/962603,
* but you can of course also use an SVG filter (heh) */
filter: invert(100%) sepia(100%) saturate(10000%) hue-rotate(30deg) brightness(50%);
}
<span class="icon square"></span>
<span class="icon circle"></span>
<span class="icon triangle"></span>
<span class="icon circle highlight"></span>
<span class="icon triangle large info"></span>
My best guess is to use data uris, which have pretty great browser support. Via something like Grunticon or their web app Grumpicon.
The output is 2 css files and 1 js that should work seamlessly with your CDN.
The rendered output is very flexible and customizable.
I had pretty much the same problem. This probably doesn't satisfy the FOMI requirement, but it's an interesting hack that got me out of a bind. Basically, this script just swaps every img in the DOM that imports an svg with inline SVG, so you can style it how you want.
// replaces img tags with svg tags if their source is an svg
// allows SVGs to be manipulated in the DOM directly
// 💡 returns a Promise, so you can execute tasks AFTER fetching SVGs
let fetchSVGs = () => {
//gets all the SRCs of the SVGs
let parentImgs = Array.from(document.querySelectorAll('img')).map((img) => {
if(img.src.endsWith('.svg')) {
return img
}
});
let promises = [];
parentImgs.forEach((img) => {
promises.push(
fetch(img.src).then((response) => {
// Error handling
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
// saves the SVG
return response.text();
})
)
});
// All fetch() calls have been made
return Promise
.all(promises)
.then((texts)=> {
texts.forEach((text, i) => {
let img = parentImgs[i];
let div = document.createElement('div');
div.innerHTML = text;
img.parentNode.appendChild(div);
let svg = div.firstChild;
img.parentNode.appendChild(svg);
// makes the SVG inherit the class from its parent
svg.classList = img.className;
// removes the junk we don't need.
div.remove();
img.parentNode.removeChild(img);
})
})
.catch((error) => {
console.log(error);
})
};
Otherwise, I came across this on Twitter today
https://twitter.com/chriscoyier/status/1124064712067624960
and applying this CSS to a div allowed me to make a colourable svg icon that can be stored in a CDN
.icon-mask {
display: inline-block;
width: 80px;
height: 80px;
background: red;
-webkit-mask: url(https://cdnjs.cloudflare.com/ajax/libs/simple-icons/3.0.1/codepen.svg);
-webkit-mask-size: cover;
}
Browser support isn't perfect yet though.
Hope this helps someone 😄
for cache you can try HTML5 app cache
https://www.w3schools.com/html/html5_app_cache.asp

Multiline text on HTML5 canvas

So I'm trying to create a Meme generator using the HTML5 canvas. I understand I can write on the canvas using the following:
ctx.filltext()
However, I can only write on the canvas once. Does anyone know how I could write on the canvas more than once or even twice as in multiple times. As with Meme generators they have one line of text at the top and one at the bottom of the image. If it's not possible could anyone let me know or point me in the direction of a tutorial/answer that could let me write on top of an image with text including changing the font and font size plus top/bottom, left/right.
Thanks to anyone who can help.
You can create a function that draws text given your desired styling arguments:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
drawText('This is the top',canvas.width/2,20,24,'verdana');
drawText('This is the bottom',canvas.width/2,canvas.height-20,16,'Courier');
function drawText(text,centerX,centerY,fontsize,fontface){
ctx.save();
ctx.font=fontsize+'px '+fontface;
ctx.textAlign='center';
ctx.textBaseline='middle';
ctx.fillText(text,centerX,centerY);
ctx.restore();
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=300></canvas>

IE display transparency bug on height > 4096px?

I was working on a JavaScript dialog with a transparent background overlay when I ran into a problem on large pages.
If the page was large, the transparent overlay would be a solid colour (i.e. no longer transparent). I did some testing and found this only happened in the overlay was greater than 4096 pixels high (hmmm, suspicious, that's 2^12).
Can anyone verify this issue? Have you seen a work-around?
Here's my test code (I'm using Prototype):
<style>
.overlayA {
position:absolute;
z-index:10;
width:100%;
height:4095px;
top:0px;
left:0px;
zoom: 1;
background-color:#000;
filter:alpha(opacity=10);
-moz-opacity:0.1;
opacity:0.1;
}
.overlayB {
position:absolute;
z-index:10;
width:100%;
height:4097px;
top:0px;
left:0px;
zoom: 1;
background-color:#000;
filter:alpha(opacity=10);
-moz-opacity:0.1;
opacity:0.1;
}
</style>
<div style="width:550px;height:5000px;border:1px solid #808080">
Display A = 4096h
<br />Display B = 4097h
</div>
<div id="overlayA" onclick="Element.hide(this)" class="overlayA" style="display:none"></div>
<div id="overlayB" onclick="Element.hide(this)" class="overlayB" style="display:none"></div>
Since you have an opacity filter on the CSS I believe you are indirectly using DirectShow under the covers for alpha blending and image composition. DirectShow uses DirectX textures, which have a 4096x4096 pixel limit for DX9, which would explain this erratic behavior.
How about making the overlay the size of the window instead of the size of the page, and moving it up or down on scroll.
You are operating at the edge already (that's huge...) so I don't know that MS would classify it as a bug or 'fix' it even if it was.
You might need to break it up into smaller overlay DIVs.
Why wouldn't you postion the overlay fixed?
That way it wouldn't have to be as big as the whole page content.
Simply doing:
#Overlay{
position:fixed;
left:0px;
top:0px;
height:100%;
width:100%;
rest of declarations
}
Just make sure it's parent is the document and the document has a width and height of 100%. That way you should be good with a much smaller overlay.
THe posotion:fixed will make sure the overlay is positioned relative to the viewport. Thus its always displayed in the top left corner.
The position:fixed solution is a spotty solution..It is not well supported in IE.
The best thing is to automatically create and append additional transparent elements (with a max height of 2048px to cover XP DX8 which has this issue as well).
Here's the code I used, assuming you already have a floating div solution.
if(document.getElementById('document_body').scrollHeight > 2048)
{
document.getElementById('float_bg').style.height = "2048px";
document.getElementById('float_bg').style.zIndex = -1;
count=1;
total_height=2048;
while(total_height < document.getElementById('document_body').scrollHeight)
{
clone = document.getElementById('float_bg').cloneNode(true);
clone.id = 'float_bg_'+count;
clone.style.zIndex = -1;
//clone.style.backgroundColor='red';
clone.style.top = (count*2048)+"px";
document.getElementById('float_el').insertBefore(clone,document.getElementById('float_bg'));
count++;
this_add = 2048;
if((total_height + 2048) > document.body.scrollHeight)
{
clone.style.height = (document.body.scrollHeight - total_height);
}
total_height += this_add;
}
}
else
{
document.getElementById('float_bg').style.height = document.body.scrollHeight + "px";
}