Rotatable Multi-stop SVG linear gradient mixin

旧巷老猫 提交于 2019-12-13 05:15:57

问题


I have created a multi-stop SVG linear gradient mixin but I am having trouble getting the SVG to scale when rotating to mimic the css3 linears-gradient spec. It works perfectly for horizontal and vertical gradients. At 45, 135, 225 and 315 degrees it also works perfectly but only if the ratio of the object is 1:1. Otherwise, it's just not quite right!

I tried playing with preserveAspectRatio="xMaxYMax meet" etc. and tried to get things working with background-size:cover or contain, but nothing would work satisfactory.

I originally tried the svg-gradient() function in LESS but it didn't do what I needed it to do (see my previous post - less-svg-gradient-function-with-multiple-variables) - so that is what led me down this path.

Does anyone have a formula that is precise to the css3 spec ??

Here is what I have so far,... I haven't found anything quite like this on the net.

.multigradient(@id: gradient;@size: contain; @direction: to right; @color){
  .case(@direction); // apply named direction values
  .case(@a) when (@a = ~"to top"){@part2:~'x1="0%" y1="100%" x2="0%" y2="0%">';@angle:@direction;@angle2: ~"bottom";}
  .case(@a) when (@a = ~"to top right"){@part2:~'x1="0%" y1="100%" x2="100%" y2="0%">';@angle:@direction;@angle2: ~"bottom left";}
  .case(@a) when (@a = ~"to right"){@part2:~'x1="0%" y1="0%" x2="100%" y2="0%">';@angle:@direction;@angle2: ~"left";}
  .case(@a) when (@a = ~"to bottom right"){@part2:~'x1="0%" y1="0%" x2="100%" y2="100%">';@angle:@direction;@angle2: ~"top left";}
  .case(@a) when (@a = ~"to bottom"){@part2:~'x1="0%" y1="0%" x2="0%" y2="100%">';@angle:@direction;@angle2: ~"top";}
  .case(@a) when (@a = ~"to bottom left"){@part2:~'x1="100%" y1="0%" x2="0%" y2="100%">';@angle:@direction;@angle2: ~"top right";}
  .case(@a) when (@a = ~"to left"){@part2:~'x1="100%" y1="0%" x2="0%" y2="0%">';@angle:@direction;@angle2: ~"right";}
  .case(@a) when (@a = ~"to top left"){@part2:~'x1="100%" y1="100%" x2="0%" y2="0%">';@angle:@direction;@angle2: ~"bottom right";}
  .case(@a) when (isnumber(@a) = true) {  // for numerical values
    @angle: unit(@a, deg);  //send degrees unchanged to standards-compliant linear-gradient
    @angle2: 90 - @angle;  //send degrees corrected to old -prefixed-linear-gradients
    .calc(@a + 90); // calculate svg co-ords
    .calc(@b) when (@b < 0){ @c: @b + 360; .calc(@c); }
    .calc(@b) when (@b >= 360){ @c: mod(@b, 360); .calc(@c); }
    .calc(@b) when (@b >= 0) and (@b < 45) {@x1: 100;@y1: 50 + (tan(unit(@b, deg)) * 50);@x2: 0;@y2: 50 - (tan(unit(@b, deg)) * 50);}
    .calc(@b) when (@b >= 45) and (@b < 90){@x1: 100 - (tan(unit(mod(@b, 45), deg)) * 50);@y1: 100;@x2: tan(unit(mod(@b, 45), deg)) * 50;@y2: 0;}
    .calc(@b) when (@b >= 90) and (@b < 135){@x1: 50 - (tan(unit(mod(@b, 45), deg)) * 50);@y1: 100;@x2: 50 + (tan(unit(mod(@b, 45), deg)) * 50);@y2: 0;}
    .calc(@b) when (@b >= 135) and (@b < 180){@x1: 0;@y1: 100 - (tan(unit(mod(@b, 45), deg)) * 50);@x2: 100;@y2: tan(unit(mod(@b, 45), deg)) * 50;}
    .calc(@b) when (@b >= 180) and (@b < 225){@x1: 0;@y1: 50 - (tan(unit(mod(@b, 45), deg)) * 50);@x2: 100;@y2: 50 + (tan(unit(mod(@b, 45), deg)) * 50);}
    .calc(@b) when (@b >= 225) and (@b < 270){@x1: tan(unit(mod(@b, 45), deg)) * 50;@y1: 0;@x2: 100 - (tan(unit(mod(@b, 45), deg)) * 50);@y2: 100;}
    .calc(@b) when (@b >= 270) and (@b < 315){@x1: 50 + (tan(unit(mod(@b, 45), deg)) * 50);@y1: 0;@x2: 50 - (tan(unit(mod(@b, 45), deg)) * 50);@y2: 100;}
    .calc(@b) when (@b >= 315) and (@b < 360){@x1: 100;@y1: tan(unit(mod(@b, 45), deg)) * 50;@x2: 0;@y2: 100 - (tan(unit(mod(@b, 45), deg)) * 50);}
    /*output: ~"svg co-ords:- x1: @{x1} y1: @{y1} x2: @{x2} y2: @{y2}";*/  // enable to print values to stylesheet (for debugging)
    @part2:~'x1="@{x1}%" y1="@{y1}%" x2="@{x2}%" y2="@{y2}%">';
  }
  .loop(length(@color), ~"");  //loop through colours and append to the variable 
  .loop(@i,@d) when (@i > 0) {
    @stop: extract(@color, @i);
    @offset: extract(@stop, 2) ;
    @stopcolor: extract(@stop, 1) ;
    @part3:~'<stop offset="@{offset}" stop-color="@{stopcolor}" />@{d}';
    .case2(@i);
    .case2(@z) when (@z = 1){  // on final loop construct and encode svg
      @part1:~'<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none"><linearGradient id="@{id}" gradientUnits="userSpaceOnUse" ';
      @part4:~'</linearGradient><rect x="0" y="0" width="1" height="1" fill="url(#@{id})" /></svg>';
      /*output2: ~"@{part1}@{part2}@{part3}@{part4}";*/  // enable to print unencoded svg to stylesheet (for debugging)
      // Thank you to Phil Brown for B64encode, taken from - http://blog.philipbrown.id.au/2012/09/base64-encoded-svg-gradient-backgrounds-in-less/
      @dataPrefix: ~"url(data:image/svg+xml;base64,";
      @dataSuffix: ~")";
      @dataContent: ~"@{part1}@{part2}@{part3}@{part4}";
      @b64Out: ~`(function(a,b,c){function e(a){a=a.replace(/\r\n/g,'\n');var b='';for(var c=0;c<a.length;c++){var d=a.charCodeAt(c);if(d<128){b+=String.fromCharCode(d)}else if(d>127&&d<2048){b+=String.fromCharCode(d>>6|192);b+=String.fromCharCode(d&63|128)}else{b+=String.fromCharCode(d>>12|224);b+=String.fromCharCode(d>>6&63|128);b+=String.fromCharCode(d&63|128)}}return b}function f(a){var b='';var c,f,g,h,i,j,l;var m=0;a=e(a);while(m<a.length){c=a.charCodeAt(m++);f=a.charCodeAt(m++);g=a.charCodeAt(m++);h=c>>2;i=(c&3)<<4|f>>4;j=(f&15)<<2|g>>6;l=g&63;if(isNaN(f)){j=l=64}else if(isNaN(g)){l=64}b=b+d.charAt(h)+d.charAt(i)+d.charAt(j)+d.charAt(l)}return b}var d='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';return a+f(b)+c})('@{dataPrefix}','@{dataContent}','@{dataSuffix}')`;
      background: ~"@{b64Out}";  // print the mofo to the css
    }
    .loop((@i - 1),@part3);    // next iteration sends variable to be joined
  } 
  background: -moz-linear-gradient(@angle2, @color);
  background: -webkit-linear-gradient(@angle2, @color);
  background: -ms-linear-gradient(@angle2, @color);
  background: -o-linear-gradient(@angle2, @color);
  background: linear-gradient(@angle, @color);
  background-size: @size;
}

// Test cases: requires id, size, angle, at least two colorstops
// =============================================================
.my-class{
  .multigradient(gradient2; contain; 0; red 0, orange 16.6%, yellow 33.3%, green 50%, blue 66.6%, indigo 83.3%, violet 100%);
}
.my-class2{
  .multigradient(i-am-unique; contain; to top right; red 0, green 50%, blue 100%);
}
.my-class3{
  .multigradient(grad3; cover; 135; pink 0, rgba(0,0,255,1.0) 50%, #fff 100%);
}
.my-class4{
  .multigradient(grad4; contain; to top; pink 0, blue 50%, white 100%);
}
.my-class5{
  .multigradient(grad5; cover; to bottom; rgba(0,2,174,1) 0%, rgba(0,2,174,1) 22.32142857142857%, rgba(0,2,137,1) 22.32142857142857%, rgba(0,2,137,1) 32.14285714285714%, rgba(0,1,48,1) 32.14285714285714%, rgba(1,32,99,1) 60.71428571428571%, rgba(3,41,112,1) 60.71428571428571%, rgba(3,41,112,1) 64.28571428571429%, rgba(0,9,45,1) 64.28571428571429%, rgba(0,9,45,1) 96.42857142857143%, rgba(3,41,112,1) 96.42857142857143%, rgba(3,41,112,1) 100%);
}
.my-class6{
  .multigradient(browns; cover; 45; DarkKhaki 0, Khaki 4%, PaleGoldenrod 8%, PeachPuff 12%, Moccasin 16%, PapayaWhip 20%, LightGoldenrodYellow 24%, LemonChiffon 28%, LightYellow 32%, Cornsilk 36%, BlanchedAlmond 40%, Bisque 44%, NavajoWhite 48%, Wheat 52%, BurlyWood 56%, Tan 60%, RosyBrown 64%, SandyBrown 68%, Goldenrod 72%, DarkGoldenrod 76%, Peru 80%, Chocolate 84%, SaddleBrown 88%, Sienna 92%, Brown 96%, Maroon 100%);
}
//  HTML for test cases
//  ===================
<div style="border:1px solid #000;height:200px;width:200px;display:inline-block;" class="my-class"></div>
<div style="border:1px solid #000;height:200px;width:200px;display:inline-block;" class="my-class2"></div>
<div style="border:1px solid #000;height:200px;width:200px;display:inline-block;" class="my-class3"></div>
<div style="border:1px solid #000;height:200px;width:200px;display:inline-block;" class="my-class4"></div>
<div style="border:1px solid #000;height:200px;width:200px;display:inline-block;" class="my-class5"></div>
<div style="border:1px solid #000;height:200px;width:400px;display:inline-block;" class="my-class6"></div>

Just cut and paste into something like codepen


回答1:


Finally I have a solution to my problem! I have created a LESS rotatable multi-stop linear gradient SVG mixin with aspect ratio to keep rotation true. More powerful in my honest opinion than the SVGs generated by Colorzilla or the MS gradient maker as it sets the x1 y1 x2 and y2 values as well as viewBox so you can rotate the gradient precisely without distortion. :)

All you need to do to generate a SVG gradient is as little as this,... (ID, angle, colorstops, ratio)

.multigradient(uniqueID; 45; #fff 0, #000 100%; 2, 1);

Rotatable Multi-stop SVG linear gradient mixin on Codepen



来源:https://stackoverflow.com/questions/26527870/rotatable-multi-stop-svg-linear-gradient-mixin

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!