HTML5 canvas overlay transparent gradients - html

I'm creating a color picker in HTML5 like the gradient below
It consists of three elements:
A solid red background color (must be changeable)
A black – transparent gradient from bottom to top
A white – transparent gradient from the left to right
I have managed to create a single gradient and a single color, but I can't figure out how to overlay the solid color and two gradients together. How can I accomplish this?
(I could provide my code but it's pretty generic and wouldn't be useful)

You can overlay two gradients on top of each other:
Fiddle demo
Horizontal gradient
Going from white to the color (HUE degree) you want to use with 100% saturation and 50% lightness:
var grH = ctx.createLinearGradient(0, 0, ctx.canvas.width, 0);
grH.addColorStop(0, '#fff');
grH.addColorStop(1, 'hsl(' + hue + ', 100%, 50%)');
ctx.fillStyle = grH;
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
Vertical
Going from black bottom to transparent at top.
var grV = ctx.createLinearGradient(0, 0, 0, ctx.canvas.height);
grV.addColorStop(0, 'rgba(0,0,0,0)');
grV.addColorStop(1, '#000');
ctx.fillStyle = grV;
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
Result
Drawing the horizontal first and then the vertical on top will result in this:
As in the demo it's easy to create a slider to update the hue palette. You don't need to re-create the black to transparent gradient as in the demo - just cache it to an off-screen canvas and re-use it for each update as that gives you a bit more performance.
Hope this helps.

I do not think that the color picker would be "correct" using the method you came up with. Because the white-transparent gradient would make the left bottom corner white but it should be black.
I created a jsfiddle based on this answer. It is still tilted but I think you can figure this out yourself:
function draw(hue){
var canvas = document.getElementById("hsl-square");
var ctx = canvas.getContext('2d');
for(row=0; row<=100; row++){
var grad = ctx.createLinearGradient(0, 0, 100,0);
grad.addColorStop(0, 'hsl('+hue+', 0%, '+(row)+'%)');
grad.addColorStop(1, 'hsl('+hue+', 100%, '+(row/2)+'%)');
ctx.fillStyle=grad;
ctx.fillRect(0, row, 100, 1);
console.log(row, row/2);
}
}
draw(64);
There is also a new html5 color input that works in most modern browsers: http://jsfiddle.net/4FMJM/1/
<form>
Select color: <input type="color" name="your-color">
<input type="submit">
</form>

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

Apply a dashed border to a masked Image in SwiftUI

I have an Image that is masked by an image asset. I'd like to apply a dashed border to the Image so that the dashed border shape follows the mask whatever it is.
Image(uiImage: self.helper.loadedImage!)
.resizable()
.scaledToFill()
.foregroundColor(Color.clear)
.background(Color.clear)
.frame(width: 200, height: 200, alignment: .center)
.clipped()
.mask(Image(uiImage: self.helper.loadedMaskImage!)
.resizable()
.frame(width: 200, height: 200, alignment: .center))
This image is rendered correctly. If loadedMaskImage is a circle for example, i see the original image in a circle shape. Now i'd like to show a circle dashed border on this image. But I don't want to use any specific shape, because the loadedMaskImage can be anything at runtime.
Is this possible without diving back into UIKit UIView?
EDIT:
This is how i can mask the view. In this case i'm using a circle image as a mask and the result is correct. Now on top of this i'd like to have a dashed border that has the same shape of that circle.
The dashed border can be achieved by using a dashed stroke of color over the image using a ZStack
Circle()
.stroke(Color.blue, style: StrokeStyle(lineWidth:5 , lineCap: .round, dash:[10, 5]))
.frame(width: 200, height: 200)
The dash argument is an array where here it means for every 10 length of showing the border there should be 5 length of no border which appears as dashed border,
I set the width and height of the frame exactly equal to your image's width and height so that it looks as though the circle is just bordering your image
You can set the first property to anything like Color.black.opacity(0.4) or even a LinearGradient if you wish so...

CanvasJS Transparent Background

I'm trying to add transparency to my CanvasJS background.
CanvasJS uses backgroundColor in order to choose the color of the background of the canvas.
I tried writing "None" instead of a color code but it's giving me a black background.
Also it would be nice to have the ability to choose different opacities for the background.
To set transparent background you can set backgroundColor: "transparent". And if you like to give transparency to any color you can use rgba color coding, for example: backgroundColor: "rgba(225,150,150,0.5)".
You can set background-color: transparent; and it will show no color.
Opacity can be set by using rgba. For white 50%, it would be background-color: rgba(255, 255, 255, .5);
I've never used CanvasJs and don't know how that script will effect things but in basic CSS these will do the trick.
I found the answer eventually. You can just have the backgroundColor with en empty string (backgroundColor:'') and it will omit the background color and leave it transparent.
I would still like to know if it's possible to add transparency to a color, so if anyone knows, please comment.
According to Canvasjs document you will be able to set background color, use rgba(0, 0, 0, 0) for transparent (of course you can change the inside values to fix your purpose):
window.onload = function () {
var chart = new CanvasJS.Chart("chartContainer",
{
title:{
text: "Title with Background Color",
padding: 5,
backgroundColor: rgba(0, 0, 0, 0),
},
data: [
{
type: "column",
dataPoints: [
{ x: 10, y: 71 },
]
}
]
});
chart.render();
}
Hope this help :)
<br/><!-- Just so that JSFiddle's Result label doesn't overlap the Chart -->
<div id="chartContainer" style="height: 300px; width: 100%;"></div>
Full demo http://jsfiddle.net/canvasjs/7b0xdcwv/

HTML hash url anchor to be centered in the page

I am wondering how can I override the HASH url behavior. Right now when I use #comment122 it will take me to comment 122 in my page, but it will place the #comment122 very high on top, and half the li is not showing. Here is an example:
<ul>
<li><a id="comment122">comment content</a></li>
<li><a id="comment123">comment content</a></li>
</ul>
Basically, what I want to achieve is every time I visit a hash url, I need it to scroll a few pixels more so the element will be centered in the page. Any advice?
You'll need to use JavaScript for this. Get the CSS top property of your element first
var scroll_object = $("#comment122").offset();
var scroll_height = scroll_object.top;
If you want to center your object in the middle of the page, you'll need to scroll the page less than the actual top property value of the object.
var scroll_height = scroll_height - 300;
The above will set the scroll to 300px above your element.
Finally, scroll the page.
window.scrollBy(0, scroll_height);
This solution uses jQuery :)
When the click event is triggered, you need to get the height of you target element (assuming you're only scrolling vertically), subtract it from window.innerHeight and divide it by two. Then you need to subtract that from the element's offset relative to the top of the window. That would give you the exact amount of pixels to vertically centralize the element on the screen.
Assuming you're using jQuery, here's a simple example:
$(document).on('click', 'li a', function(e) {
e.preventDefault();
var target = $($(this).attr('href'));
$('html, body').animate({
scrollTop: target.offset().top - parseInt((window.innerHeight - target.outerHeight()) / 2, 10)
}, 300);
});
Here's a fiddle with the working example: http://jsfiddle.net/FJBXg/
The same logic could be used to intercept a loading page with a hash on it's URL, so you could animate the page to the element on the URL's hash. But I'll leave that to you.
2021 Method
Below is a method that uses new web features. It blocks the normal link action and scrolls smoothly to a centered position.
You might need to use this polyfill on browsers that aren't super new.
document.querySelector('a[href^="#"]').addEventListener('click', (e) => {
e.preventDefault()
document
.querySelector(e.currentTarget.getAttribute('href'))
.scrollIntoView({
behavior: 'smooth',
block: 'center'
})
})
.filler {
/* CSS Pattern by Logan McBroom (http://logan.mcbroom.me/) */
background: linear-gradient(63deg, #999 23%, transparent 23%) 7px 0, linear-gradient(63deg, transparent 74%, #999 78%), linear-gradient(63deg, transparent 34%, #999 38%, #999 58%, transparent 62%), #444;
background-size: 16px 48px;
height: 5000px;
}
<a id="firstlink" href="#thespot">Scroll <i>smooth</i> and centered.</a>
<br/><br/>
Scroll classic <b>janky headbutt</b> style.
<br/><br/>
<div class="filler"></div>
<p id="thespot">jump to me!</p>
<div class="filler"></div>

How can I make concentric circles in HTML respond to mouseOver properly?

On my web page, I'd like to have a group of several concentric circles of varying size, with each displaying a menu when they are moused over.
So far, I have created 4 pictures that is 100% transparent except for the circle and have layered them on top of each other. However, when I mouse-over the group of circles, the transparent part of the top image causes the mouse-over event to fire. How can I deal with this?
For reference, here is the code that I have so far.
<html>
<head>
<style type="text/css">
img
{
position:fixed;
left: 0;
bottom: 0;
}
</style>
<title>Circle Test</title>
</head>
<body>
<img src="Circle2.png" onMouseover="alert('Button2')"/>
<img src="Circle4.png" onMouseover="alert('Button4')"/>
<img src="Circle3.png" onMouseover="alert('Button3')"/>
<img src="Circle1.png" onMouseover="alert('Button1')"/>
</body>
</html>
This would be hard with pure images as it's difficult to tell when the mouse is actually over the circle part of the image. I would suggest a client side image map as they let you define clickable areas in non-rectangular shapes. Set the href to something like "javascript:circleClicked(); void 0;" :D
There's no way to tell that the mouse is over a transparent pixel of your circle: you must check if the mouse intersects with the actual circle (yeah, really). Actually, that's easier than it might seem. Considering your circle's diameter is your image's width (which appears quite possible to me), you just have to check that the mouse cursor is within the radius of the circle (which would be width / 2):
function intersectsCircle(diameter, center, mousePosition)
{
var radius = diameter / 2;
// compute the distance between mousePosition and center
var deltaX = mousePosition.x - center.x;
var deltaY = mousePosition.y - center.y;
return Math.sqrt(deltaX * deltaX + deltaY * deltaY) < radius;
}
And the you just have to pass the required informations (my Javascript's rusty so what follows might not be exactly accurate, especially double-check the events part):
function intersects(target, event)
{
var center = {x: target.x + target.width / 2, y: target.y + target.height / 2};
var mousePosition = {x: event.clientX, y: event.clientY};
if(intersectsCircle(target.width, center, mousePosition))
alert('Foo');
}
<img onmouseover="intersects(this, event)" src="circle.png" />