web-dev-qa-db-ja.com

線形勾配の背景位置でパーセント値を使用する

background-positionがパーセント値を取る方法はありますか?現在、私のボタンはwidthおよびbackground-positionの明示的な値でのみ機能します。

body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  align-items: center;
}

.button {
  display: flex;
  justify-content: center;
  align-items: center;
  text-decoration: none;
  color: white;
  font-weight: bold;
  width: 350px;
  height: 50px;
  border: 1px solid green;
  transition: background 0.5s;
  background-repeat: no-repeat;
  background-image: linear-gradient(to left, #2484c6, #1995c8 51%, #00bbce), linear-gradient(to right, #2484c6 0%, #1995c8 51%, #00bbce 76%);
}
.button-pixel {
  background-position: -350px 0px, 0px 0px;
}
.button-pixel:hover {
  background-position: 0px 0px, 350px 0px;
}
.button-percentage {
  background-position: -100% 0px, 0px 0px;
}
.button-percentage:hover {
  background-position: 0% 0px, 100% 0px;
}
<a href="#" class="button button-pixel">In Pixel</a>
<a href="#" class="button button-percentage">In Percentage</a>
14
Mo.

TL; DR

グラデーションを背景として使用する場合、background-positionで使用されるすべてのパーセント値同等ですなので、違いは見られません。コンテナサイズとは異なるbackground-sizeを指定する必要があります。

body {
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  align-items: center;
  min-height:90vh;
}

.button {
  text-decoration: none;
  color: white;
  font-weight: bold;
  width: 350px;
  height: 50px;
  text-align:center;
  transition: background 0.5s;
  background-repeat: no-repeat;
  background-image: 
    linear-gradient(to left, #2484c6, #1995c8 51%, #00bbce), 
    linear-gradient(to right, #2484c6 0%, #1995c8 51%, #00bbce 76%);
  background-size:200% 100%; /*Changed this*/
}
.button-pixel {
  background-position: -350px 0px, 0px 0px;
}
.button-pixel:hover {
  background-position: 0px 0px, 350px 0px;
}
.button-percentage {
  background-position: 100% 0px, 0px 0px;
}
.button-percentage:hover {
  background-position: 0% 0px, 100% 0px;
}
<a href="#" class="button button-pixel">Pixel</a>
<a href="#" class="button button-percentage">Percentage</a>

バックグラウンドポジションはどのように機能しますか?

background-positionがどのように機能するかを説明するために、クラシックイメージを使用してみましょう。

ピクセル値を使用する場合、参照はサイズに関係なく、画像の上/左隅です。これは、配置された要素でtop/leftを使用するようなものです。

.b {
  width:200px;
  height:200px;
  background:url(https://picsum.photos/100/100?image=1069) no-repeat;
  border:1px solid;
  background-position:0 0;
  position:relative;
  animation:back 5s infinite linear alternate;
}
.b:before {
  content:"";
  position:absolute;
  top:0;
  left:0;
  width:10px;
  height:10px;
  background:red;
  animation:change 5s infinite linear alternate;
}

@keyframes back{to{background-position:200px 200px;}}
@keyframes change{to{top:200px; left:200px;}}
<div class="b"></div>

パーセント値を使用する場合、参照はピクセル値を使用する場合とは異なります。それはもはや上/左隅ではありません:

.b {
  width:200px;
  height:200px;
  background:url(https://picsum.photos/100/100?image=1069) no-repeat;
  border:1px solid;
  background-position:0% 0%;
  position:relative;
  animation:back 3s infinite linear alternate;
}
.b:before {
  content:"";
  position:absolute;
  top:0;
  left:0;
  width:10px;
  height:10px;
  background:red;
  animation:change 3s infinite linear alternate;
}
@keyframes back{to{background-position:100% 100%;}}
@keyframes change{to{top:200px; left:200px;}}
<div class="b"></div>

この場合、2つのパラメーターを考慮する必要があります。コンテナーのサイズと画像のサイズです。これがどのように機能するかを示した図です(私はbackground-position30% 30%と同じにした):

enter image description here

まず、画像を検討して、画像を配置するために使用する基準点を見つけます。 画像のサイズを考慮して、上/左隅から30% 30%に位置するのは画像の内側の点です(緑の線で定義されているように)。次に、そのポイントコンテナの内側を上/左隅から30% 30%コンテナのサイズを考慮して配置します。

このロジックから、次のような些細なケースを明確に特定できます。

50% 50%(中央)100% 100%(右下)100% 0%(右上)

enter image description here

画像のサイズがコンテナのサイズと等しいの場合、すべての位置が同等であるだけでは何も起こりません。画像の上部/左は既にコンテナの上部/左(0%0%)にあり、中央はすでに中央(50%50%)にあります。

.b {
  width:200px;
  height:200px;
  background:url(https://picsum.photos/200/200?image=1069) no-repeat;
  border:1px solid;
  background-position:0% 0%;
  position:relative;
  animation:back 5s infinite linear alternate;
}
.b:before {
  content:"";
  position:absolute;
  top:0;
  left:0;
  width:10px;
  height:10px;
  background:red;
  animation:change 5s infinite linear alternate;
}
@keyframes back{to{background-position:100% 100%;}}
@keyframes change{to{top:100%; left:100%;}}
<div class="b"></div>

グラデーションは画像と見なされるため、上記のロジックはグラデーションに適用した場合と同じです。デフォルトでは、background-sizeを指定しない場合、画像を使用する場合とは異なり、グラデーションのサイズはコンテナのサイズになります。

background-size指定 を参照すると、問題がどのように発生するかがわかります。

両方の値が自動である場合、-固有の幅または高さ、またはその両方画像を使用する必要があります(存在する場合)、欠落している寸法(存在する場合)は、上記のように自動として動作します。 。画像に固有の幅も固有の高さもない場合、そのサイズはcontainの場合と同様に決定されます。

そして:

含む

固有のアスペクト比(存在する場合)を維持しながら、画像を拡大縮小して、幅と高さの両方が背景の配置領域に収まるようにします。

そしてまた:

ビットマップ画像(JPGなど)常に組み込みのサイズと比率。

CSS <gradient>s 固有の寸法はありませんまたは固有の比率参照

画像には常に固有の値があるため、ほとんどの場合、コンテナーと同じサイズにはならないため、パーセント単位のbackground-positionが効果を発揮します。ただし、グラデーションには固有の値がないため、グラデーションのディメンションはそのコンテナーのサイズと等しくなり、コンテナーのディメンションとは異なるbackground-positionを指定しない限り、パーセンテージ値を持つbackground-sizeは機能しません。



より詳細な

上記の例では、background-size0%の間の値を使用する場合に100%がどのように機能するかを見ましたが、負の値または100%より大きい値を使用する場合はどうでしょうか?ロジックは同じですが、参照点を見つけるのはより難しくなります。

負の値(<0%)

-50% 0に背景を配置するとします。この場合、基準点は画像の外側になります。次に例を示します。

.b {
  width:200px;
  height:200px;
  border:1px solid;
  background:url(https://picsum.photos/100/100?image=1069) -50% 0/100px 100px no-repeat;
}
<div class="b"></div>

enter image description here

図でわかるように、まず基準点(つまり、画像の左端から-50px)を定義するために、画像の-50%、つまり-50pxを検討します。次に、コンテナーのサイズ(コンテナーの左端から-100px)を考慮して、そのポイントを-50%に配置します。次に画像を描画し、上記の結果を取得します。画像の100pxのみが表示されます。

また、イメージのサイズがコンテナーのサイズより小さい場合、負のパーセンテージ値は負の固定値と同じように動作します(どちらもイメージが左にシフトします)。この場合、-50% 0-50px 0と同じです。

.b {
  width:200px;
  height:200px;
  display:inline-block;
  border:1px solid;
  background:url(https://picsum.photos/100/100?image=1069) -50% 0/100px 100px no-repeat;
}
.a{
  background:url(https://picsum.photos/100/100?image=1069) -50px 0/100px 100px no-repeat;
}
<div class="b">
</div>
<div class="b a">
</div>

たとえば、画像サイズを150px 150pxに増やすと、-50% 0-25px 0と同じになります。

サイズコンテナよりも大きいにすると、負の値は画像を右にシフトし始めます(正のピクセル値の場合と同様)。これは、画像の50%が増加する一方で50%が増加するため、論理的ですコンテナは同じままです。

前の図を考えると、下の線よりも大きくなるまで上の緑の線を増やしているようなものです。したがって、記号のみでは、背景画像がどのようにシフトするかを知るには不十分です。サイズも考慮する必要があります。

.b{
  width:200px;
  height:200px;
  border:1px solid;
  background:url(https://picsum.photos/300/300?image=1069) -50% 0/50px 50px no-repeat;
  animation:change 2s linear infinite alternate; 
}
@keyframes change{
  to {background-size:300px 300px}
}
<div class="b">
</div>

グラデーションについても同じことが論理的に起こります。

.b {
  width:200px;
  height:200px;
  border:1px solid;
  background:linear-gradient(to right,red,blue) -50% 0/50px 150px no-repeat;
  animation:change 2s linear infinite alternate; 
}
@keyframes change{
  to   {background-size:300px 150px}
}
<div class="b">
</div>

大きな値(> 100%)

以前と同じロジック:150% 0で背景を定義する場合、参照ポイントを左端から150%(または右端から50%)と見なし、コンテナーの左端から150%を配置します。

.b {
  width:200px;
  height:200px;
  border:1px solid;
  background:url(https://picsum.photos/100/100?image=1069) 150% 0/100px 100px no-repeat;
}
<div class="b"></div>

この場合、150% 0150px 0と同等であり、背景のサイズを増やし始めると、前に示したのと同じ動作になります。

.b {
  width:200px;
  height:200px;
  border:1px solid;
  background:url(https://picsum.photos/300/300?image=1069) 150% 0/50px 50px no-repeat;
  animation:change 2s infinite linear alternate;
}
@keyframes change {
  to {background-size:300px 300px}
}
<div class="b"></div>


特殊なケース

[0% 100%]の範囲外の値を使用すると、背景画像を非表示にすることができますが、画像を完全に非表示にするには、正確な値をどのように見つけるのですか?

以下の図を考えてみましょう:

enter image description here

画像の幅はWsで、コンテナの幅はWpであり、pの値を見つける必要があります。この図から、次の式を得ることができます。

p * Wp = p * Ws + Ws   <=>   p = Ws/(Wp - Ws)   where p in [0,1]

コンテナーのサイズが200pxで、画像が100pxの場合、p1なので、100%になります(もちろん、負の符号を追加すると-100%になります)。

固定値の代わりにbackground-sizeを使用してパーセンテージ値を考慮する場合、これをより一般的にすることができます。 background-sizeS%であるとします。次に、Ws = Wp * s (s in [0,1] and S=s*100%)があり、式は

p = Ws/(Wp - Ws)   <=>   p = s / (1 - s)

マイナス記号を追加すると、p = s / (s - 1)になります。

右側の画像を非表示にしたい場合は、右側でも同じロジックを実行します(前の図のミラーを検討します)。ただし、常に左側のエッジを検討して、追加する必要があるパーセンテージを見つけるために100%

新しいパーセンテージp'%100% + p%であり、式はp' = 1 + p --> p' = 1 + s / (1 - s) = 1 / (1 - s)になります。

上記の計算を説明するアニメーションを次に示します。

.b {
  width:200px;
  height:50px;
  margin:5px;
  border:1px solid;
  background:linear-gradient(to right,red,blue) no-repeat;
  background-size:calc(var(--s) * 100%) 100%;
  animation:change 4s linear infinite alternate;
}
@keyframes  change{
   from { /*Hide on the left*/
     background-position:calc(var(--s)/(var(--s) - 1) * 100%)
   }
   to { /*Hide on the right*/
     background-position:calc(1/(1 - var(--s)) * 100%)
   }
}
<div class="b" style="--s:0.5">
</div>
<div class="b" style="--s:0.8">
</div>
<div class="b" style="--s:2">
</div>

いくつかの値を計算してみましょう:

s=0.5の場合、background-size50%と等しくなり、パーセント値は-100%から200%になります。この場合、イメージのサイズはコンテナーのサイズよりも小さいであるため、負の値で開始し、正の値で終了しました。最後のケース(s=2)を考慮すると、background-size200%に等しく、パーセント値は200%から-100%までになります。 イメージのサイズがコンテナーのサイズより大きいであるため、正の値で開始し、負の値で終了しました。

これは前に言ったことを確認します。画像を左にシフトするには、サイズが小さい場合は負の値が必要ですが、サイズが大きい場合は正の値が必要です(右も同じです)。


ピクセルとパーセント値の関係

ピクセル値に基づいてパーセント値を計算する方法、またはその逆(つまり、両方の間で変換する式)を定義する方法を定義しましょう。これを行うには、参照点を考慮する必要があります。

enter image description here

ピクセル値を使用する場合、青い線を考慮し、background-position:X Yを使用します。

パーセンテージ値を使用する場合、緑色の線を考慮してbackground-position:Px Pyを作成します。

数式は次のようになります。Y + Py * Ws = Py * Wpここで、Wsは画像の幅で、Wpはコンテナの幅です(高さを考慮したX軸と同じ数式)。

Y = Py * (Wp - Ws)があります。この式から、前に説明したように2つの点を検証できます。

  • Wp = Wsの場合、式は無効になり、イメージのサイズがコンテナーと同じである場合、パーセンテージ値は効果がないことが確認されます。したがって、ピクセル値とパーセンテージ値の間に関係はありません。
  • YPyは、Wp > Wsの場合は同じ符号を持ち、Wp < Wsの場合は反対の符号を持ちます。これは、パーセンテージ値が画像のサイズに応じて異なる動作をすることを確認しています。

background-sizeのパーセント値を考慮すると、式を別の方法で表現することもできます。 Y = Py * Wp * (1-s)があります。

上記の計算を説明するアニメーションを次に示します。

.b {
  width:200px;
  height:50px;
  margin:5px;
  border:1px solid;
  background:linear-gradient(to right,red,blue) no-repeat;
  background-size:calc(var(--s) * 100%) 100%;
  animation:percentage 2s linear infinite alternate;
}
.box.a {
  animation-name:pixel; 
}
@keyframes  percentage{
   from { background-position:-50%;}
   to { background-position:150%;}
}
@keyframes  pixel{
   from { background-position:calc(-0.5 * 200px * (1 - var(--s))) }
   to {  background-position:calc(1.5 * 200px * (1 - var(--s)));}
}
<div class="b" style="--s:0.5">
</div>
<div class="b a" style="--s:0.5">
</div>

<div class="b" style="--s:2">
</div>
<div class="b a" style="--s:2">
</div>


参照の変更

上記の計算では、ピクセル値またはパーセンテージ値のいずれかにロジックを適用するために、画像とコンテナーの左上隅を常に考慮しました。この参照は、background-positionにさらに値を追加することで変更できます。

デフォルトでは、background-position: X Ybackground-position: left X top Yと同等です(Xからleftに、Yからtopに位置)。 topleftを調整することで、参照と画像の配置方法を変更します。ここではいくつかの例を示します。

.b {
  width:150px;
  height:150px;
  display:inline-block;
  background:url(https://picsum.photos/70/70?image=1069) no-repeat;
  border:1px solid;
  position:relative;
}

body {
 margin:0;
}
<div class="b"></div>
<div class="b" style="background-position:left 0 bottom 0"></div>
<div class="b" style="background-position:right 0 bottom 0"></div>
<div class="b" style="background-position:right 0 top 0"></div>


<div class="b" style="background-position:right 10% top 30%"></div>
<div class="b" style="background-position:right 10% bottom 30%"></div>
<div class="b" style="background-position:right 10px top 20px"></div>
<div class="b" style="background-position:left 50% bottom 20px"></div>

Xの値には、leftright(水平位置)しか使用できず、Yの値では、bottomtop(垂直位置)しか使用できないことは明らかです。すべての異なる組み合わせで、4つの異なるコーナーを論理的に取得できます。

この機能は、計算を最適化するためにも役立ちます。 特別なケースセクションの例では、最初の計算を行って左側の画像を非表示にし、次に別の画像を右側の画像を非表示にしました。参照の変更を検討する場合、必要な計算は1つだけです。左側で使用されている式を使用し、右側でも同じ式を使用します。

ここに新しいバージョンがあります:

.b {
  width:200px;
  height:50px;
  margin:5px;
  border:1px solid;
  background:linear-gradient(to right,red,blue) no-repeat;
  background-size:calc(var(--s) * 100%) 100%;
  animation:change 4s linear infinite alternate;
}
@keyframes  change{
   from { 
     background-position:left  calc(var(--s)/(var(--s) - 1) * 100%) top 0
   }
   to { 
     background-position:right calc(var(--s)/(var(--s) - 1) * 100%) top 0
   }
}
<div class="b" style="--s:0.5">
</div>
<div class="b" style="--s:0.8">
</div>
<div class="b" style="--s:2">
</div>

s=0.5の場合、-100%から200%へのアニメーションは行われませんが、left -100%からright -100%へのアニメーションになります。

ピクセル値を使用した別の例を次に示します。参照を変更するときに計算がどれほど簡単に処理できるかを明確に確認できます。

.b {
  width:200px;
  height:200px;
  background:url(https://picsum.photos/100/100?image=1069) no-repeat;
  border:1px solid;
  background-repeat:no-repeat;
  animation:change 2s infinite linear;
}


@keyframes change{
  0%{background-position:left 20px top 20px;}
  25%{background-position:right 20px top 20px;}
  50%{background-position:right 20px bottom 20px;}
  75%{background-position:left 20px bottom 20px;}
  100%{background-position:left 20px top 20px;}
}
<div class="b"></div>

同じ参照を維持して同じアニメーションを実現するのは難しいでしょう。したがって、対称的なアニメーションを作成する場合は、ロジックを片側で実行し、参照を変更することで反対側でも同じように使用します。


ピクセル値とパーセント値の組み合わせ

CSS3では、calc()を使用して、異なる単位を含む複雑な計算を行うことができます。たとえば、width:calc(100px + 20% + 12em)と書くと、ブラウザーは各ユニットの動作を考慮して計算値を計算し、ピクセル値(この場合)で終了します。

background-positionはどうですか? calc(50% + 50px)と書いた場合、これはパーセント値またはピクセル値に評価されますか?ピクセル値はパーセンテージまたはその逆に変換されますか?

結果はピクセル値またはパーセンテージ値に変換されませんが、両方が一緒に使用されます! background-positionは、calc()内でパーセンテージとピクセル値を混合するときに特別な動作をし、ロジックは次のとおりです。

  1. 最初に、パーセンテージ値に関連するすべてのロジックを適用することにより、パーセンテージ値を使用して画像を配置します。
  2. (1)の位置を参照として考慮し、ピクセル値に関連するすべてのロジックを適用して、ピクセル値を使用して画像を再度配置します。

したがって、calc(50% + 50px)は、画像を中央に配置してから、左に50pxシフトすることを意味します。

この機能により、多くの計算を簡略化できます。次に例を示します。

.b {
  width:200px;
  height:200px;
  display:inline-block;
  border:1px solid;
  background-image:
    linear-gradient(red,red),
    linear-gradient(red,red),
    linear-gradient(red,red),
    linear-gradient(red,red);
 background-size:20px 20px;
 background-position:
    calc(50% + 20px) 50%,
    calc(50% - 20px) 50%,
    50% calc(50% - 20px),
    50% calc(50% + 20px);
 background-repeat:no-repeat;
 transition:0.5s;
}
.b:hover {
  background-position:50%;
}
<div class="b"></div>
<div class="b" style="width:100px;height:100px;"></div>

上記のように4つの赤い正方形を配置するための正しいパーセンテージまたはピクセル値を見つけるのは面倒ですが、calc()を使用して両方を混合することは非常に簡単です。

ここで、calc(10% + 20px + 30% + -10px + 10% + 20px)のようなものがあるとします。ブラウザはこれをどのように処理しますか?

そのような場合、ブラウザはまず各ユニットを評価して、簡略化されたフォームcalc(X% + Ypx)を取得し、次に上記のロジックを適用して画像を配置します。

calc(10% + 20px + 30% + -10px + 10% + 20px) 
calc((10% + 30% + 10%) + (20px + -10px +20px)) 
calc(50% + 30px)
.box {
  display:inline-block;
  width:200px;
  height:200px;
  background-image:url(https://picsum.photos/100/100?image=1069);
  border:1px solid;
  background-position:calc(10% + 20px + 30% + -10px + 10% + 20px) 0;
  background-repeat:no-repeat;
}
.alt {
  background-position:calc(50% + 30px) 0;
}
 
<div class="box"></div>
<div class="box alt"></div>

数式の複雑さが何であれ、ブラウザは常にパーセンテージとピクセル値を別々に評価します。


background-Originを使用する

次に、背景画像の位置を変更するために使用できるもう1つの重要なプロパティを示します。このプロパティはボックスモデルに依存しているので、それがどのように機能するかを簡単に思い出させます。

enter image description here

各要素の内部には、border-box、padding-box、content-boxの3つの異なるボックスがあります。 background-Originは、以前のロジックをすべて実行するために考慮する必要があるボックスを指定します。

以下は一目瞭然の例です:

.b {
  display:inline-block;
  width:200px;
  height:200px;
  background:
    url(https://picsum.photos/100/100?image=1069) no-repeat,
    linear-gradient(to right,red,blue) bottom/100% 20% no-repeat;
  border:20px solid rgba(0,0,0,0.1);
  padding:20px;
  box-sizing:border-box;

  background-Origin:border-box;
}
.p {
  background-Origin:padding-box; /*the default value*/
}
.c {
  background-Origin:content-box;
}
<div class="b"></div>
<div class="b p"></div>
<div class="b c"></div>

パディングがない場合、content-boxpadding-boxと同等であり、ボーダーがない場合、border-boxpadding-boxと同等であることは明らかです。


パーセンテージの動作を変える

本当に画像のサイズをコンテナのサイズと等しくする必要があり、ピクセルのようなパーセンテージを使用して移動する必要がある場合は、以下のアイデアを検討できます。

  • 疑似要素を背景レイヤーとして使用:
.b {
  width:200px;
  height:200px;
  border:1px solid;
  position:relative;
  z-index:0;
  overflow:hidden;
}
.b:before {
  content:"";
  position:absolute;
  top:0;
  left:0;
  width:100%;
  height:100%;
  z-index:-1;
  background:url(https://picsum.photos/200/200?image=1069);
  background-size:100% 100%;
  transition:1s;
}
.b:hover::before {
  transform:translate(100%,100%);
}
<div class="b"></div>

変換では疑似要素のサイズを考慮しますが、コンテナーと同じであるため、問題はありません。また、left/topを使用することもできますが、transformを使用するとパフォーマンスが向上します。

  • background-Originの使用

秘訣は、パディングを用意し、Originをcontent-boxに制限し、100%よりもサイズを大きくしてパディングをカバーし、画像をコンテナに収めることです。

.b {
  width:200px;
  height:200px;
  outline:1px solid;
  padding:0 100px 100px 0;
  box-sizing:border-box;
  z-index:0;
  overflow:hidden;
  background:url(https://picsum.photos/200/200?image=1069) no-repeat;
  background-Origin:content-box;
  background-size:200% 200%;
  transition:0.8s;
}

.b:hover {
  background-position:-200% -200%; 
  /* We use [0%,-200%] to cover [0%,100%]*/
}
<div class="b"></div>

上記では、パディングを半​​分のサイズにしたので、論理的に200%background-sizeを使用して修正する必要があります。 background-positionでは、上記の説明に基づいて、必要な値を簡単に見つけることができます。

もう一つの例:

.b {
  width:200px;
  height:200px;
  outline:1px solid;
  padding:50px;
  box-sizing:border-box;
  z-index:0;
  overflow:hidden;
  background:url(https://picsum.photos/200/200?image=1069) no-repeat;
  background-Origin:content-box;
  background-size:200% 200%;
  background-position:50% 50%;
  transition:0.8s;
}

.b:hover {
  background-position:-150% -150%; 
  /* We use [50%,-150%] to cover [0%,100%]*/
}
<div class="b"></div>



emchなどの他のユニットは、pxと同じように動作します。それらは lengths と呼ばれます。

38
Temani Afif