1. ホーム
  2. Web プログラミング
  3. CSS/HTML

CSSで曲線の描画とアニメーションを表示する方法

2022-01-18 07:30:35

ボックスシャドウを理解する

まず、box-shadowプロパティをおさらいしておきましょう。基本的なプロパティの使い方は、要素に影をつけることです。

そして、この記事で使用する影に関する最初のトリックについて簡単に触れておきます。

影を利用した画像/投影のコピー

box-shadowの第3、第4パラメータをともにblur、expand radius 0にすると、要素と同じ大きさの影を得ることができる。

div {
    width: 80px;
    height: 80px;
    border: 1px solid #333;
    box-sizing: border-box;
    box-shadow: 80px 80px 0 0 #000;
}

以下のような結果が得られました。

シャドウは複数可能

2つ目のトリックは、box-shadowでは複数のシャドウが可能で、その座標は完全に制御可能であることです。

そう、ある要素に対して複数のシャドウを定義し、シャドウの第1、第2パラメータを使って要素に対する相対座標をこのように制御することができるんだ。

div {
    width: 80px;
    height: 80px;
    border: 1px solid #333;
    box-sizing: border-box;
    box-shadow:
        80px 80px 0 0 #000,
        70px 70px 0 0 #000,
        ...
        60px 60px 0 0 #000;
}

シェーディング座標で三角関数を使用する

続けて 次に、陰影座標における三角関数の導入に挑戦します。

なぜ、円の標準方程式や楕円の標準方程式などの図形関数ではなく、三角関数なのでしょうか?もちろん、三角関数のcosやsinを使えば、CSSで直接実現するのが難しいカーブを実現できることを除けば、うまくいくでしょう。

それを踏まえて、次のような曲線を実装したいと仮定して、リストを進めてみましょう。

CSSを使用する場合の解決策は?

クリップパスや、text-decorationのアンダーラインの波形を利用したトリック、グラデーションのオーバーレイなどを利用することが考えられます。

もちろん、この記事で紹介するbox-shadowや三角関数を使うというアプローチもあります。

三角関数

エヘン、sin, cos曲線のイメージ変換の中の三角関数の簡単な復習、まだ全部は先生に返していません。

1x1 divで、サイン・コサイン関数のイメージのように複数の影を並べることができれば、曲線までつながるのではないでしょうか?

CSSで三角関数sin/cosを使用する方法

それは良いアイデアだが、CSSは三角関数そのものを提供していない。ここでは、Sassを使ってCSSに簡単な三角関数を実装する必要があります。

幸いなことに、誰かがすでにこれをやってくれています。

  • sassで三角法
  • Sassで三角関数の計算を実装する

簡単に言うと、Sass関数は三角関数のテイラー展開の助けを借りて、sin()、cos()、tan()の実装をシミュレートしているのです。

展開が無限に長いので、Sass関数シミュレーションを使う場合、あまり正確な値を得ることはできませんが、Sass関数シミュレーションを使った三角関数の sin(), cos(), tan() のように、日常のグラフ作成には全く問題ないでしょう。

@function fact($number) {
    $value: 1;
    @if $number>0 {
        @for $i from 1 through $number {
            $value: $value * $i;
        }
    }
    @return $value;
}
 
@function pow($number, $exp) {
    $value: 1;
    @if $exp>0 {
        @for $i from 1 through $exp {
            $value: $value * $number;
        }
    }
    @else if $exp < 0 {
        @for $i from 1 through -$exp {
            $value: $value / $number;
        }
    }
    @return $value;
}
 
@function rad($angle) {
    $unit: unit($angle);
    $unitless: $angle / ($angle * 0 + 1);
    @if $unit==deg {
        $unitless: $unitless / 180 * pi();
    }
    @return $unitless;
}
 
@function pi() {
    @return 3.14159265359;
}
 
@function sin($angle) {
    $sin: 0;
    $angle: rad($angle);
    // Iterate a bunch of times.
    @for $i from 0 through 20 {
        $sin: $sin + pow(-1, $i) * pow($angle, (2 * $i + 1)) / fact(2 * $i + 1);
    }
    @return $sin;
}
 
@function cos($angle) {
    $cos: 0;
    $angle: rad($angle);
    // Iterate a bunch of times.
    @for $i from 0 through 20 {
        $cos: $cos + pow(-1, $i) * pow($angle, 2 * $i) / fact(2 * $i);
    }
    @return $cos;
}
 
@function tan($angle) {
    @return sin($angle) / cos($angle);
}

上記のsinとcosのテイラー展開の最終計算は20レベルのループしか使っていないので、入力される値が大きすぎると大きな誤差が生じます。それでも[-20, 20]以内の値であれば非常に高い精度が得られます。

例えばsin関数はxを[-π, π]の間で受け取りますが、これはすべてのsin(x)の値の範囲をカバーしているので、[-20, 20]の範囲は完全に適切で、入ってくるx値があまり誤差なくこの領域に入るようにすればいいのです。

さて、それでは、次のような構造を想定して、上のsin関数を使ってみましょう。

div {
    width: 1px;
    height: 1px;
    background: #000;
    border-radius: 50%;
}

次に、Sassで50レベルのループを実装します。もちろん、影のx座標はsin関数を使用します。

@function shadowSet($vx, $vy) {
    $shadow : 0 0 0 0 0 #000;
     
    @for $i from 0 through 50 {
        $x: sin($i / 8) * $vx;
        $y: $i * $vy;
         
        $shadow: $shadow, #{$x} #{$y} 0 0 0 rgba(0, 0, 0, 1);
    }
     
    @return $shadow;
}
 
div {
    width: 1px;
    height: 1px;
    background: #000;
    border-radius: 50%;
    box-shadow: shadowSet(4px, 1px);
}

上の sin($i / 8) を 8 で割ると、sin(x) の範囲が [0, 2π] のとき、完全に一つの曲線を描くのにちょうど良い [0, 6.25] になります。この8は、ループの数によって調整することができる。

実際には、次のようにボックスシャドウを得る。

{
    box-shadow:
    0 0 0 0 0 black, 0.4986989335px 1px 0 0 black, 0.989615837px 2px 0 0 0 black,
    1.4650901163px 3px 0 0 0 black, 1.9177021544px 4px 0 0 0 black, 2.3403890918px 5px 0 0 0 black,
    2.7265550401px 6px 0 0 black, 3.0701740089px 7px 0 0 black, 3.3658839392px 8px 0 0 black,
    3.6090703764px 9px 0 0 black, 3.7959384774px 10px 0 0 black, 3.9235722281px 11px 0 0 black,
    3.9899799464px 12px 0 0 black, 3.9941253622px 13px 0 0 black, 3.9359437875px 14px 0 0 black,
    3.8163431264px 15px 0 0 black, 3.6371897073px 16px 0 0 black, 3.4012791593px 17px 0 0 black,
    3.1122927876px 18px 0 0 black, 2.7747401278px 19px 0 0 black, 2.3938885764px 20px 0 0 black,
    1.9756811944px 21px 0 0 black, 1.5266439682px 22px 0 0 black, 1.0537839735px 23px 0 0 black,
    0.5644800322px 24px 0 0 black, 0.0663675689px 25px 0 0 black, -0.4327805381px 26px 0 0 black,
    -0.9251752496px 27px 0 0 black, -1.4031329108px 28px 0 0 black, -1.8591951521px 29px 0 0 black,
    -2.286245275px 30px 0 0 black, -2.677619305px 31px 0 0 black, -3.0272099812px 32px 0 0 black,
    -3.3295620582px 33px 0 0 black, -3.5799574329px 34px 0 0 black, -3.7744887692px 35px 0 0 black,
    -3.9101204707px 36px 0 0 black, -3.9847360499px 37px 0 0 black, -3.9971711559px 38px 0 0 black,
    -3.9472317429px 39px 0 0 black, -3.8356970987px 40px 0 0 black, -3.6643076841px 41px 0 0 0 black,
    -3.4357379737px 42px 0 0 black, -3.1535547213px 43px 0 0 black, -2.8221613023px 44px 0 0 black,
    -2.446729px 45px 0 0 black, -2.03311631px 46px 0 0 black, -1.58777752px 47px 0 0 black,
    -1.1176619928px 48px 0 0 black, -0.630105724px 49px 0 0 black, -0.1327168662px 50px 0 0 black;
}

実際に得られた画像は以下の通りです。

コントロールカラーと初期方向

上記のSassによって実装された@function shadowSet($vx, $vy)メソッドを見てください。$vx, $vyは画像の振幅とゆるさを制御するために使われ、初期方向を制御するために$direction、影の層の数を制御する$count、色を制御する$colorを追加しています。

@function shadowSet($vx, $vy, $direction, $count, $color) {
    $shadow : 0 0 0 0 0 $color;
     
    @for $i from 0 through $count {
        $x: sin($i / 8) * $vx * $direction;
        $y: $i * $vy;
         
        $shadow: $shadow, #{$x} #{$y} 0 0 0 $color;
    }
     
    @return $shadow;
}
.line {
    width: 1px;
    height: 1px;
    margin: 10vh auto;
    background: #000;
    border-radius: 50%;
    box-shadow: shadowSet(4px, 1px, 1, 50, #000);
}
 
.reverseline {
  • adjust-hue($color,$degrees): creates a new color by changing the hue value of a color.
  • lighten($color,$amount): creates a new color by changing a color's brightness value to make it lighter.
  • darken($color,$amount): creates a new color by changing the brightness value of the color to make it darker.
  • saturate($color,$amount): creates a new color by changing the saturation value of the color to make it more saturated
  • desaturate($color,$amount): creates a new color by changing the saturation value of the color so that it is less saturated.

OK, look at the effect this time.

@function fact($number) {
    $value: 1;
    @if $number>0 {
        @for $i from 1 through $number {
            $value: $value * $i;
        }
    }
    @return $value;
}

In the css-doodle use

OK, all of the preceding is laid out to use it in some actual creative ideas.

In css-doodle, since it's a Web Component feature. When you need trigonometric functions, it's easier to use the Math functions provided by JavaScript.

{Web Components Web Components are a different set of web technologies that allow you to create reusable custom elements (whose functionality is encapsulated outside of your code) and use them in your web applications.

Yuan Chuan, the author of the css-doodle library, uses a pure CSS painting using the above technique in his Codepen homepage backdrop.

html, body {  
  height: 100%;  
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #000; 
} 

<style>
  css-doodle {
    --color: @p(#010059, #52437b, #ff7a8a, #fcf594)@p([2-9a-d])@lp();
    --rule: (
      :doodle {
        @grid: 8 / 120vmin 80vmin;
        max-width: 100vw;
        background: linear-gradient(#ff7a8a, #fcf594);
        filter: hue-rotate(-22deg);
        overflow: hidden;
      }
      @size: @r(4vmin, 15vmin);
      mix-blend-mode: multiply;
      transform: translate(@m2(@r(-80%, 100%)));
      border-radius: 50%;
      --n: @p(-1, 1);
      --c: var(--color);
      box-shadow: @m100(
        calc(@sin(@n() / 10) * 1.8vmin * @var(--n))
        calc(@n() * 1vmin) 0
        @var(--c)
      );
      background: @m(@p(0, @ri(500)), (
        radial-gradient(var(--color) 50%, transparent 0)
        @r(100%) @r(100%) / @r(1px, 3px) @lr()
        no-repeat
      ));
      background-color: var(--color);
    );
  }   
</style>
<css-doodle use="var(--rule)" click-to-update></css-doodle>

I also tried to use this technique and made a pair of

summary

There are a few points that need to be mentioned.

1, why must use box-shadow, directly stacked div can not?

Yes, you can use multiple box-shadows just because it saves tags and one div. Even more, if you're willing to toss and turn, it's okay to use multiple gradients.

2. Are the above two demos purely CSS-drawn?

Yes, they are. Although we use the css-doodle library, they are both CSS code, but this library encapsulates a lot of ready-to-use functions. css-doodle

3. What's the use?

Well, whether it's useful or not is a philosophical question. At least I think it's interesting.

The above is the details of how to draw curves and display animations in CSS, more information about drawing curves and display animations in CSS, please pay attention to other related articles in the Codedevlib!