Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to set the gradient start and end positions in a CSS background similar to SVG start and end positions?

In an SVG gradient you can set the start x y and end x y position. Is it possible to do that in CSS.

Here is the original design:

enter image description here

Here is my SVG with linear gradient:

.myRectangle {
	width: 331px;
	height: 137px;
	left: 0px;
	top: 0px;
}
<svg class="myRectangle">
  <linearGradient id="LinearGradientFill2" spreadMethod="pad" x1="1" x2="0.5" y1="0" y2="0.5">
    <stop offset="0" stop-color="#f7f7f7" stop-opacity="1" cssvalue=""></stop>
    <stop offset="0.266" stop-color="#ea0000" stop-opacity="1" cssvalue=""></stop>
    <stop offset="0.7685" stop-color="#6c165f" stop-opacity="1" cssvalue=""></stop>
    <stop offset="1" stop-color="#272020" stop-opacity="1" cssvalue=""></stop>
  </linearGradient>
  <rect fill="url(#LinearGradientFill2)" id="myRectangle" rx="0" ry="0" x="0" y="0" width="331" height="137">
  </rect>
</svg>

Here is my CSS linear gradient:

#rectangle {
  width: 100%;
  height: 200px;
  position: absolute;
  top: 0;
  left: 0;
  background: linear-gradient(225deg, rgba(255,255,255,1) 0%, rgba(250,0,0,1) 27.59%, rgba(108,22,95,1) 76.35%, rgba(39,32,32,1) 100%)
}
<div id="rectangle">

</div>

I've been referencing this page on MDN and this page on W3C.

The SVG contains the orientation of the gradient

x1="1" x2="0.5" y1="0" y2="0.5"

The element also takes several other attributes, which specify the size and appearance of the gradient. The orientation of the gradient is controlled by two points, designated by the attributes x1, x2, y1, and y2. These attributes define a line along which the gradient travels. The gradient defaults to a horizontal orientation, but it can be rotated by changing these. Gradient2 in the above example is designed to create a vertical gradient. - from https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Gradients

From other documentation:

X and Y position of the start of the gradient line, as a multiple of the object's bounding box: X=0 indicates the left edge of the bounding box and X=1 indicates the right edge. The gradient line may start or end outside the object's bounding box, so values may be < 0 or > 1.

like image 496
1.21 gigawatts Avatar asked Aug 15 '19 20:08

1.21 gigawatts


People also ask

Can SVG files have gradients?

SVG provides for two types of gradients: linear gradients and radial gradients. Once defined, gradients are then referenced using 'fill' or 'stroke' properties on a given graphics element to indicate that the given element shall be filled or stroked with the referenced gradient.

Is it possible to combine a background image and CSS3 gradients?

You can combine a background-image and CSS3 gradient on the same element by using several backgrounds. In our example below, we set the height and width of our <div> and add a background. Then, we set the background image fallback using the “url” value of the background-image property.

How do you change the direction of the gradient in CSS?

Using Angles If you want more control over the direction of the gradient, you can define an angle, instead of the predefined directions (to bottom, to top, to right, to left, to bottom right, etc.). A value of 0deg is equivalent to "to top". A value of 90deg is equivalent to "to right".

How do you SVG a gradient?

To use a gradient, we have to reference it from an object's fill or stroke attributes. This is done the same way you reference elements in CSS, using a url . In this case, the url is just a reference to our gradient, which I've given the creative ID, "Gradient". To attach it, set the fill to url(#Gradient) , and voila!


1 Answers

You can divide all the value of color stop by the same factor in order to reduce the size of the gradient and make it similar to the SVG one.

I used CSS variable to make it easy but it's not mandatory

.rectangle {
  width: 100%;
  height: 200px;
  --s:2;
  background: 
   linear-gradient(225deg, 
    rgba(255,255,255,1) 0%, 
    rgba(250,0,0,1)   calc(27.59%/var(--s)), 
    rgba(108,22,95,1) calc(76.35%/var(--s)), 
    rgba(39,32,32,1)  calc(100%/var(--s)));
}
<div class="rectangle">

</div>

<div class="rectangle" style="--s:1.5">

</div>

<div class="rectangle" style="--s:3">

</div>

You can add an extra variable to offset the gradient which will simulate the starting point:

.rectangle {
  width: 100%;
  height: 200px;
  --s:2;
  --p:10%;
  background: 
   linear-gradient(225deg, 
    rgba(255,255,255,1) var(--p), 
    rgba(250,0,0,1)   calc(27.59%/var(--s) + var(--p)), 
    rgba(108,22,95,1) calc(76.35%/var(--s) + var(--p)), 
    rgba(39,32,32,1)  calc(100%/var(--s) + var(--p)));
}
<div class="rectangle">

</div>

<div class="rectangle" style="--s:3;--p:20%">

</div>

<div class="rectangle" style="--s:3">

</div>

Here is some comparaison between SVG and CSS gradient:

.rectangle {  
  background: 
   linear-gradient(var(--a), 
    #f7f7f7  var(--p), 
    #ea0000  calc(26.6%/var(--s) + var(--p)), 
    #6c165f  calc(76.85%/var(--s) + var(--p)), 
    #272020  calc(100%/var(--s) + var(--p)));
}

.myRectangle,
.rectangle {
  width:100px;
  height:100px;
  display:inline-block;
}
<svg class="myRectangle" viewBox="0 0 100 100">
  <linearGradient id="LinearGradientFill2" spreadMethod="pad" x1="0.7" x2="0.5" y1="0.2" y2="0.5">
    <stop offset="0" stop-color="#f7f7f7"  ></stop>
    <stop offset="0.266" stop-color="#ea0000" ></stop>
    <stop offset="0.7685" stop-color="#6c165f" ></stop>
    <stop offset="1" stop-color="#272020" ></stop>
  </linearGradient>
  <rect fill="url(#LinearGradientFill2)"  rx="0" ry="0" x="0" y="0" width="100" height="100">
  </rect>
</svg>
<div class="rectangle" style="--s: 3.82;--p: 23.87%;--a:213.69deg;">

</div>
<svg class="myRectangle" viewBox="0 0 100 100">
  <linearGradient id="LinearGradientFill3" spreadMethod="pad" x1="0.7" x2="1" y1="0.2" y2="0.8">
    <stop offset="0" stop-color="#f7f7f7"  ></stop>
    <stop offset="0.266" stop-color="#ea0000"  ></stop>
    <stop offset="0.7685" stop-color="#6c165f"  ></stop>
    <stop offset="1" stop-color="#272020"  ></stop>
  </linearGradient>
  <rect fill="url(#LinearGradientFill3)"  rx="0" ry="0" x="0" y="0" width="100" height="100">
  </rect>
</svg>
<div class="rectangle" style="--s: 1.98;--p: 33%;--a: 153.5deg;">

</div>
<svg class="myRectangle" viewBox="0 0 100 100">
  <linearGradient id="LinearGradientFill4" spreadMethod="pad" x1="0.2" x2="0.5" y1="1" y2="0.8">
    <stop offset="0" stop-color="#f7f7f7" ></stop>
    <stop offset="0.266" stop-color="#ea0000"></stop>
    <stop offset="0.7685" stop-color="#6c165f" ></stop>
    <stop offset="1" stop-color="#272020"  ></stop>
  </linearGradient>
  <rect fill="url(#LinearGradientFill4)"  rx="0" ry="0" x="0" y="0" width="100" height="100">
  </rect>
</svg>
<div class="rectangle" style="--s: 3.84;--p: 12%;--a: 56.3deg;">

</div>
<svg class="myRectangle" viewBox="0 0 100 100">
  <linearGradient id="LinearGradientFill5" spreadMethod="pad" x1="0.9" x2="0.2" y1="1" y2="0.8">
    <stop offset="0" stop-color="#f7f7f7" ></stop>
    <stop offset="0.266" stop-color="#ea0000" ></stop>
    <stop offset="0.7685" stop-color="#6c165f" ></stop>
    <stop offset="1" stop-color="#272020" ></stop>
  </linearGradient>
  <rect fill="url(#LinearGradientFill5)"  rx="0" ry="0" x="0" y="0" width="100" height="100">
  </rect>
</svg>
<div class="rectangle" style="--s: 1.7;--p: 10%;--a: -74.05deg;">

</div>

You keep the color and percetange values the same. For the SVG you adjust x,y values and for the CSS you adjust the variables (the angle, the offset and the divider)

The formula of the angle is like below:

angle = arctang(Height*(x2 - x1)/Width*|y2 - y1|)

if y2 > y1 we do an extra step to have angle = 180deg - angle

To find the divider we need to consider the length of the CSS gradient which is equal to

Dc = |Width * sin(angle)| + |Height * cos(angle)|

and the length of the SVG gradient which is equal to:

Ds = sqrt(Width²*(x2 - x1)² + Height²*(y2 - y1)²)

Then we do a simple division Dc/Ds

The formula of the offset p is ((Dc/2 - d)*100) / Dc where Dc is the length of the CSS gradient defined previously and d is equal to:

d = (Width²*(x1-0.5)*(x1-x2)+Height²*(y1-0.5)*(y1-y2))/(sqrt(Width²*(x2-x1)²+Height²*(y2-y1)²))

Here is an interactive demo

function update() {
 var H = $('[name=h]').val();
 var W = $('[name=w]').val();
 var x1 = $('[name=x1]').val();
 var x2 = $('[name=x2]').val();
 var y1 = $('[name=y1]').val();
 var y2 = $('[name=y2]').val();
 $('#LinearGradientFill2').attr('x1',x1);
 $('#LinearGradientFill2').attr('x2',x2);
 $('#LinearGradientFill2').attr('y1',y1);
 $('#LinearGradientFill2').attr('y2',y2);
 $('polyline').attr('points',(x1*W)+','+(y1*H)+' '+(x2*W)+','+(y2*H));
 
 var angle = Math.atan((H*(x2 - x1))/Math.abs(W*(y2 - y1))) 
 if(y2 > y1)
    angle=Math.PI - angle;
 $('.rectangle').css("--a", (angle * 180 / Math.PI)+"deg");
 var Dc = Math.abs(W*Math.sin(angle)) + Math.abs(H*Math.cos(angle));
 var Ds = Math.sqrt(W*W*(x2 - x1)*(x2 - x1) + H*H*(y2 - y1)*(y2 - y1));
 var s=Dc/Ds;
 $('.rectangle').css("--s", s+'');
 var d = (W*W*(x1 - 0.5)*(x1 - x2)+H*H*(y1 - 0.5)*(y1 - y2))/Math.sqrt(W*W*(x2 - x1)*(x2 - x1) + H*H*(y2 - y1)*(y2 - y1));
 $('.rectangle').css("--p", (((Dc/2 - d)*100) / Dc)+"%");
 
 // update elements
 $('[name=a]').val(Math.floor(angle * 180 / Math.PI));
 $('rect').attr("width", W);
 $('.rectangle').css("width", W);
 $('.myRectangle').css("width", W);
 $('rect').attr("height", H);
 $('.rectangle').css("height", H);
 $('.myRectangle').css("height", H);
 $('.myRectangle').attr("viewBox", "0 0 " + W + " " +H);
};

$('input').change(update);
update();
.rectangle {  
  background: 
   linear-gradient(var(--a,0deg), 
    #f7f7f7  var(--p,0%), 
    #ea0000  calc(26.6%/var(--s,1) + var(--p,0%)), 
    #6c165f  calc(76.85%/var(--s,1) + var(--p,0%)), 
    #272020  calc(100%/var(--s,1) + var(--p,0%)));
}

.myRectangle,
.rectangle {
  width:150px;
  height:100px;
  display:inline-block;
  margin:10px;
  border:1px solid green;
}

input {
 width:50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
W: <input type="number" name="w" step="1" value="100">
H: <input type="number" name="h" step="1" value="100">
A: <input type="number" name="a" disabled>
<br>
X1: <input type="number" name="x1" step="0.1" value="1">
X2: <input type="number" name="x2" step="0.1" value="0.5">
Y1: <input type="number" name="y1" step="0.1" value="0">
Y2: <input type="number" name="y2" step="0.1" value="0.5">
<br>
<svg class="myRectangle" >
  <linearGradient id="LinearGradientFill2" spreadMethod="pad" x1="0" x2="0" y1="1" y2="0">
    <stop offset="0" stop-color="#f7f7f7"></stop>
    <stop offset="0.266" stop-color="#ea0000"></stop>
    <stop offset="0.7685" stop-color="#6c165f"></stop>
    <stop offset="1" stop-color="#272020"></stop>
  </linearGradient>
  <rect id="rect" fill="url(#LinearGradientFill2)" rx="0" ry="0" x="0" y="0" width="150" height="100">
  </rect>
  <polyline stroke="green" points="0,1 0,0 "/>
</svg>
<div class="rectangle">

</div>

The above gives perfect result when dealing with a square shape. For a rectangular shape there is still some difference. Checking the formula ...

like image 195
Temani Afif Avatar answered Nov 15 '22 21:11

Temani Afif