How is a CSS gradient path calculated? - css

When using a linear gradient such as
div {
width: 300px;
height: 50px;
background: linear-gradient(to right, blue, red);
}
<div></div>
the colors could be changed via different paths.
In the example above, it could be done by just modifying linearly the R and B channel, without touching to the G one -- but the variation could also be non-linear (to provide, say, a sense of linearity because it would be more physiological), or by tinkering with the G channel (again because it might seem to be a more realistic 'red to blue transition' to our eyes).
What is the formula used in linear-gradient to switch between two colors?

Gradients in HTML/CSS are linear interpolations, purely mathematical. Per the W3C canvas spec:
Once a gradient has been created (see below), stops are placed along it to define how the colors are distributed along the gradient. The color of the gradient at each stop is the color specified for that stop. Between each such stop, the colors and the alpha component must be linearly interpolated over the RGBA space without premultiplying the alpha value to find the color to use at that offset. Before the first stop, the color must be the color of the first stop. After the last stop, the color must be the color of the last stop. When there are no stops, the gradient is transparent black.
SVGs work the same way.
CSS gradients are the same, except for one difference (emphasis mine):
Between two color stops, the line’s color is interpolated between the colors of the two color stops, with the interpolation taking place in premultiplied RGBA space.
So all three use linear interpolation, and canvas/SVG use non-premultiplied alpha while CSS uses premultiplied alpha (which does look nicer). As to why that makes a difference, see this example:
html, body, svg, div {
display: block;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background: white;
}
svg {height: 60%;}
div.gradient {
height: 20%;
margin-top: 0.2%;
background: linear-gradient(to right,
rgba(0%, 100%, 0%, 1),
rgba(0,0,0,0));
}
<svg>
<linearGradient id="a">
<stop offset="0" stop-color="lime"
stop-opacity="1" />
<stop offset="1" stop-color="lime"
stop-opacity="0" />
</linearGradient>
<linearGradient id="b">
<stop offset="0" stop-color="lime"
stop-opacity="1" />
<stop offset="1" stop-color="black"
stop-opacity="0" />
</linearGradient>
<linearGradient id="c">
<stop offset="0" stop-color="rgba(0%, 100%, 0%, 1)" />
<stop offset="1" stop-color="rgba(0,0,0,0)" />
</linearGradient>
<rect width="100%" height="33%"
fill="url(#a)" />
<rect width="100%" height="33%" y="33.5%"
fill="url(#b)" />
<rect width="100%" height="33%" y="67%"
fill="url(#c)" />
</svg>
<div class="gradient"></div>
<ul>
<li>Top: SVG gradient with constant stop-color and transitioned stop-opacity;</li>
<li>2nd: SVG gradient with stop-color transitioning to black and stop-opacity transitioning to zero;</li>
<li>3rd: SVG gradient with rgba colors;</li>
<li>Bottom: CSS gradient with the same rgba colors.</li>
</ul>
<p>All transition from opaque lime to fully transparent; in all but the first SVG gradient, the final stop is transparent black. The CSS gradient scales the intensity of the color by the alpha value before transitioning, so you don't get the fade to gray effect.</p>
Disclaimer: That's not my snippet! I took it from this CodePen example, but SO won't let me link to it without adding code myself.

Related

Is it possible to make a gradient color of a stroke that composes a SVG image

I would like to know if is it possible to do a gradient in the stroke color that appears in the two lines below, and is set to #389967. This is a SVG
.chart-three svg .circle-foreground {
stroke: #389967;
stroke-dasharray: 494.55px 549.5px;
stroke-dashoffset: 494.55px;
stroke-linecap: round;
-webkit-transform-origin: 50% 50%;
transform-origin: 50% 50%;
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
}
THe full source code can be found here, it's on scss, but you can compile it to CSS :
https://codepen.io/kunalkamble/pen/XXbWwN
I'm looking to change the foreground color of this chart, in order to get it gradient, do you know if that is possible ?
I would like to measure like a meter of energy consumption, when is very close to 0% is green and when it gets close to 100% is red
thanks in advance
This is doable using an image stored as an SVG pattern for your stroke, with the caveat being that you can no longer round the edges. The stroke caps technically go past the start of your element, so a sliver of the 100% value of the gradient (red) peeks in unless you remove it.
https://codepen.io/will0220/pen/xXjKry
<svg role="img" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="fillColors" patternUnits="userSpaceOnUse" width="200" height="200">
<image xlink:href="http://willmurdoch.com/images/strokeGrad.jpg" width="200" height="200" />
</pattern>
<linearGradient id="linear" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="red"/>
<stop offset="100%" stop-color="green"/>
</linearGradient>
</defs>
</svg>
Then use that as the foreground color. If you plan on using the same image I did, please save it down from the link above and re-upload it somewhere else, since it's hosted on my personal server.

jqwidgets radial guage styling background

I am using jqwidgets. In those widgets i am using radial gauge. For that radial gauge i want to give radial background colour. i have got a code something like this for changing background color in radial gauge.
$('#gauge').jqxGauge({ style: { fill: '#cccccc', stroke: '#cccccc' }});
In the above code i have tried something like this,
$('#gauge').jqxGauge({ style: { fill: 'linear-gradient(to top, #000 19%, #000 41%, #232323 49%, #fff)', stroke: 'linear-gradient(to top, #000 19%, #000 41%, #232323 49%, #fff)' }});
But this isnt working. Please help me.
Thank you.
I don't think CSS gradients work as SVG fills.
So you will need to define your gradients in an SVG you embed in the page. Then you can reference them for your gauge.
So, for instance, the equivalent SVG gradient definition for your fill one would be:
<svg width="0" height="0">
<defs>
<linearGradient id="gaugefill" x1="0" y1="1" x2="0" y2="0">
<stop offset="19%" stop-color="#000"/>
<stop offset="41%" stop-color="#000"/>
<stop offset="49%" stop-color="#232323"/>
<stop offset="100%" stop-color="#fff"/>
</linearGradient>
</defs>
</svg>
Put this in your page somewhere. Then you should be able to apply it to your guage with:
$('#gauge').jqxGauge({ style: { fill: 'url(#gaugefill)', stroke: '#ccccc` }});
I'll leave the stroke one for you to do.
PS. Disclaimer: I have not used these widgets, so this is an educated guess only.

Transparent font on transparent background

I am currently working on a scrollbar which looks just like this. If this text is to long for you just skip to the example!
There will be arrows at the left and right side to scroll either left or right. The color of the scrollbar is not grey as it seems but rgba(0,0,0,0.6). So its a transparent black, which will be used in front of images.
Now I want the last few letters in the line to fade out like in this example. To accomplish that I am using a div overlay with:
background-image: linear-gradient(to right, rgba(255,255,255,0), black)
But if I would do that with a transparent scrollmenu, it would mess up the background-color of the scrollmenu (if you don´t know what I mean look here).
So I found a solution by combining two divs with linear-gradients on the very right, which, if you lay them over each other, create exactly the background color of the scrollmenu. One of these lays behind the font, one of them overlays the font. That way I can achieve some transparency on the font. Here is an example for you guys:
#rightPart, #leftPart{
width:200px; height:50px;
position:absolute; top:0;left:0;
color: white;
}
#rightPart{
z-index:-1;
background:linear-gradient( to right, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
}
#leftPart{
background:linear-gradient( to left, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
}
<div id="rightPart"> Slight Transparency effect on this </div>
<div id="leftPart"></div>
The problem is that the transparency effect is limited to the background transparency of 0.5. Therefore the transparency effect can´t get as strong as I want it to be.
Now I am asking for a solution, with which I could achieve a stronger transparency effect. I would appreciate your suggestions.
Please remember that I can´t just make a specific word in the end transparent, since there is always a different word in the end of the scrollbar! Consequently I would need to make the font itself in a specific area transparent. And I personally don´t know how to do that (especially if it is supposed to work on all of the newest browser versions - including IE9+).
<svg> filters are just what your looking for.
IE9 here to ruin your day CAN I USE IT?
Now to adjust the filter simply change the offset of the stop elements inside the linearGradiant.
offset to determine where the effect takes place
stop-color and stop-opacity to determine what the effect should be
example:
<br>
<svg height="40" width="200" viewbox="0 0 100 20" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="endfade" x1="0%" y1="0%" x2="100%" y2="0">
<stop offset="50%" stop-opacity="1" />
<stop offset="90%" stop-opacity="0" />
</linearGradient>
</defs>
<text id="spesial" fill="url(#endfade)" x="0" y="15">Your text here</text>
</svg>
<br>Color?
<br>
<svg height="40" width="200" viewbox="0 0 100 20" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="pinkblue" x1="0%" y1="0%" x2="100%" y2="0">
<stop offset="25%" stop-opacity="1" stop-color="pink" />
<stop offset="50%" stop-opacity="1" stop-color="aqua" />
<stop offset="90%" stop-opacity="0" stop-color="aqua" />
</linearGradient>
</defs>
<text id="spesial" fill="url(#pinkblue)" x="0" y="15">Your text here</text>
</svg>
Well, I can not offer a solution with full browser support.
But since there isn't any other answer, may be it can be useful
First, let's create a base div that will hold the semitransparent color.
Above it, let's set a div with the text. overlayed by a gradient from transparent to gray. this makes the right side of the div fully gray.
The trick is to set for this div a mix-blend-mode of hard-light. This makes gray behave as transparent, keeping black as black
#base {
left: 0px;
width: 200px;
height: 50px;
position: relative;
background: rgba(0,0,0,0.5);
}
#text {
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
mix-blend-mode: hard-light;
}
#text:after {
content: "";
position: absolute;
left: 0px;
top: 0px;
height: 100%;
width: 100%;
background: linear-gradient(90deg, transparent 30%, gray 80%);
}
#base:nth-child(2) {
background: rgba(0,0,0,0.25);
}
#base:nth-child(3) {
background: rgba(0,0,0,0.125);
}
<div id="base">
<div id="text"> Slight Transpareny effect on this </div>
</div>
<div id="base">
<div id="text"> Slight Transpareny effect on this </div>
</div>
<div id="base">
<div id="text"> Slight Transpareny effect on this </div>
</div>

SVG working on html image tag -- not on HTML5 canvas

I have a problem here. I need something to be implemented onclick of a button. I searched on the net and found out that SVG was an easier way to do it rather than CSS stylesheets.
I have an HTML5 canvas (which is basically a snapshot from a video stream that I have taken and drawn on to the HTML 5 canvas using drawImage()). On click of a button, I want the image with the following effect to come on the canvas instead of the original image.
My problem is that I am able to replicate the effect if I apply the effect on just an image. However, on the canvas it gives me the mask separately and the image separately. This is the code that I am using for the SVG
<svg height="0">
<defs>
<mask id="mask-radial">
<rect width="500" height="500" fill="url(#g1)"></rect>
<radialGradient id="g1" cx="80%" cy="80%" r="100%">
<stop stop-color="black" offset="50%"/>
<stop stop-color="white" offset="110%"/>
</radialGradient>
</mask>
<!--<mask id="mask-linear">
<rect width="400" height="300" fill="url(#l1)"></rect>
<linearGradient id="l1" x1="0" y1="0" x2="0" y2="1">
<stop stop-color="white" offset="0%"/>
<stop stop-color="black" offset="30%"/>
<stop stop-color="white" offset="100%"/>
</linearGradient>
</mask> -->
<filter id="filtre1">
<feGaussianBlur in="SourceGraphic" stdDeviation="5"/>
</filter>
<!-- <filter id="filtre2">
<feGaussianBlur in="SourceGraphic" stdDeviation="10"/>
</filter> -->
</defs>
</svg>
This is how I am calling it in CSS
.effet{
width: 500px; height: 500px;
margin: 0 auto 50px auto;
box-shadow: 0 1px 5px rgba(0,0,0,.5);
}
.effet img{
position: absolute;
}
.filtre--r{
-webkit-mask: -webkit-radial-gradient( center, closest-side, transparent 50%, black 80%);
-webkit-mask: radial-gradient( closest-side at center, transparent 50%, black 80%);
-webkit-filter: blur(40px);
mask: url('#mask-radial');
filter: url('#filtre1');
}
and these are my HTML elements.
<div class="effet">
<img src="/static/images/noEffect.png" alt="" />
<img class="filtre filtre--r" src="/static/images/noEffect.png" alt="" />
</div>
Can somebody please tell me how I can recreate the effect for my canvas? Thanks in advance.
I researched and found that HTML5 Canvas radial gradient was an effective solution.
var lightSize = 100;
var x = 200;
var y = 200;
var radialGradient = context.createRadialGradient(x, y, 0,x, y, lightSize);
radialGradient.addColorStop(0, "#FFFF99"); // color at start circle
radialGradient.addColorStop(0.9, "#FFFF99"); //color at offset 0.9
radialGradient.addColorStop(1, '#7D7D5C');// color of finish circle
context.globalCompositeOperation = "multiply";
context.fillStyle = radialGradient;
Was able to recreate the effect totally.

Vertically-repeating horizontal gradient in CSS

I'm having trouble coming up with a pure CSS mechanism to get a particular background pattern happening.
What I'm looking for is a horizontal gradient that is also repeated vertically, with a gap between each instance. Example:
(source: howsfamily.net)
I can get the horizontal effect easily enough
background: linear-gradient(to left, white, red, white); background-size: 100% 50px; background-repeat: no-repeat; }
I can get the vertical effect (without the horizontal gradient)
background: linear-gradient(to bottom, red 0px, red 50px, transparent 50px, transparent 100%); background-size: 100% 150px; background-repeat: repeat-y;
Does anyone know how to combine the two?
Extending from comment:
Since you're already going for linear-gradient, I would suggest using an SVG for more freedom and better compatibility.
Example: http://dabblet.com/gist/6632969
The SVG that is used (beautified):
<?xml version="1.0"?>
<svg width="10" height="100" viewBox="0 0 10 100" preserveAspectRatio="none" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="l">
<stop offset="0%" stop-color="white" />
<stop offset="50%" stop-color="red" />
<stop offset="100%" stop-color="white" />
</linearGradient>
</defs>
<rect x="0" y="0" width="10" height="50" fill="url(#l)" />
</svg>
You can tweak the height and viewBox here and background-size in CSS to fit your need.
The preserveAspectRatio attribute here is crucial, otherwise the background image may not stretch.

Resources