『CSS Secrets』は、@Lea Verou による最新の本で、CSS に関する小さな秘密を説明しています。これは CSSers にとって読む価値のある本です。一定期間読んだ後、私、@全域と @彦子は、関連する読書感想文を W3cplus で公開し、皆さんと共有します。
2 つの画像の視覚的な違いを他の人に示す必要がある場合があります。通常、1 つは変更前の画像で、もう 1 つは変更後の画像です。たとえば、2 つの画像を並べて写真加工の効果を示します。たとえば、一部の美容師の Web サイトでは、特定の美容トリートメントの効果や、特定の地理的領域における災害の結果を表示したいと考えています。
最も一般的な解決策は、2 つの画像を並べて配置することです。ただし、この場合、人間の目は非常に顕著な違いしか認識できず、そのような小さな変化を検出することはできません。比較がそれほど重要ではない場合、または 2 つの画像の差が非常に大きい場合は、この方法で問題ありませんが、その他の場合には、より良い方法が必要です。
ユーザー エクスペリエンスの観点から、この問題には多くの解決策があります。一般的な解決策は、GIF または CSS アニメーションを使用して、同じ場所に 2 つの画像を続けて表示することです。これは、2 つの写真を並べて表示するよりもはるかに優れています。しかし、ユーザーは画像全体を目で捉えるまでに数回待つ必要があるため、違いを見つけるには時間がかかります。
英国の大手報道機関であるガーディアンからの、2011 年のロンドン暴動の悲惨な結果をユーザーが比較できるようにするインタラクティブな画像比較ウィジェットの例。ユーザーは 2 つの画像間の白い垂直バーをドラッグできますが、「これはドラッグできます」というメッセージは表示されません。そのため、ヘルプ テキスト (「スライダーを移動する...」) が必要です。理想的には、優れた学習可能なインターフェイスにはヘルプ テキストは必要ありません。
より実用的な解決策は、「画像比較スライダー」です。 2 つの画像を重ねて表示し、ユーザーが仕切りをドラッグしてどちらを表示するかを選択できるようにします。もちろん、そのようなコントロールは実際には HTML には存在しません。現実の要素を使ってそれをシミュレートする必要がありますが、この効果を達成する例は長年にわたって数多くあり、通常は JavaScript フレームワークと大量の JS コードが必要です。
いくつかのバリエーションでは、ユーザーはドラッグする代わりにマウスを直接動かすことができます。これの利点は、ユーザーが気づきやすく使いやすいことですが、その体験は非常にエキサイティングです。
そのような制御を実現する簡単な方法はありますか?もちろんあります、そしてそれは2つあります!
よく考えてみると、画像比較スライダーには通常、画像と、別の画像を表示するために使用される水平方向にサイズ変更可能な要素が含まれています。通常、これは JavaScript フレームワークが必要な場合であり、トップ画像を水平方向にサイズ変更可能にします。ただし、要素のサイズを変更可能にするために必ずしもスクリプトが必要というわけではありません。 CSS3 には、resize というプロパティがあります。
このプロパティについて聞いたことがなくても、
水平方向のサイズ変更は通常、ページ レイアウトを壊すため、
ここで考えられる質問: このプロパティを使用して画像スライダーを実装できるでしょうか?試してみないとどうやってわかりますか!
最初に考えられるのは、2 つの 要素を使用することです。ただし、サイズ変更を に直接適用すると非常に見苦しくなり、画像を直接サイズ変更すると歪みが生じます。したがって、それを
<div class="image-slider"><div> <img src="adamcatlace-before.jpg" alt="Before" /></div><img src="adamcatlace-after.jpg" alt="After" /></div>
object-fit と object-position がより広いブラウザーでサポートされると、背景画像のスケーリングを同じ方法で制御できるため、これは問題になりません。画像のスケーリング。
次に、位置決めとサイズ設定のためにいくつかの基本的な CSS を適用する必要があります:
.image-slider { position:relative; display: inline-block;}.image-slider > div { position: absolute; top: 0; bottom: 0; left: 0; width: 50%; /* Initial width */ overflow: hidden; /* Make it clip the image */}.image-slider img { display: block; }
結果は画像のようになりますが、これはまだ静的です。
幅を手動で変更すると、ユーザーが可能なすべての調整を確認できます。サイズ変更属性を使用してユーザーの操作に応じて幅を動的に変更するには、さらに 2 つの宣言が必要です:
.image-slider > div { position: absolute; top: 0; bottom: 0; left: 0; width: 50%; overflow: hidden; resize: horizontal;}
唯一の視覚的な変更は、サイズ変更ハンドルが上の画像の右下隅に表示されることです
我们现在可以拖动它,把它调整到我们的主要内容!但是,拖动我们的小工具时,也发现了一些缺陷:
第一个问题很容易解决。我们只需要指定 max-width 的值为 100% 。但是,第二个问题稍微复杂一点。可惜,现在还是没有标准的方法来给resize句柄添加样式。一些渲染引擎支持私有伪元素(如, ::-webkitresizer ),但是它们的结果非常受限制,不仅是在浏览器支持方面,还有样式灵活性方面。但是,希望仍在:这说明在 resize 句柄上覆盖一个伪元素不会干扰它的功能,甚至不需要设置 pointer-events: none 。所以,给 resize 句柄添加样式的跨浏览器的解决方案就是在它的上边覆盖一个伪元素。如下:
.image-slider > div::before { content: ''; position: absolute; bottom: 0; right: 0; width: 12px; height: 12px; background: white; cursor: ew-resize;}
注意 cursor: ew-resize 这条声明:它增加了一个额外的支持,因为它提示用户说这块区域可以用作一个resize句柄。但是,我们不能依赖光标改变作为我们唯一的支持,因为它们是当用户和控件交互时才是可视的。
现在,我们的resize句柄将会变成一个白色方块。
在这里,我们可以接着往下,把它变成我们喜欢的样式。例如,把它变成一个距图像边距为 5px 的白色三角形,
CSS如下:
padding: 5px;background:linear-gradient(-45deg, white 50%, transparent 0);background-clip: content-box;
为了进一步改进,我们可以给两个图像都应用 user-select: none 。这样,没有抓取到resize句柄的话也不会白白导致它们被selected。总结一下,完整的代码如下:
.image-slider { position:relative; display: inline-block;}.image-slider > div { position: absolute; top: 0; bottom: 0; left: 0; width: 50%; max-width: 100%; overflow: hidden; resize: horizontal;}.image-slider > div::before { content: ''; position: absolute; bottom: 0; right: 0; width: 12px; height: 12px; padding: 5px; background: linear-gradient(-45deg, white 50%, transparent 0); background-clip: content-box; cursor: ew-resize;}.image-slider img { display: block; user-select: none;}
在上一节中提到的CSS resize方法运行是非常ok的,而且代码量也非常小。但是,它有一些缺点:
<div class="image-slider"> <img src="adamcatlace-before.jpg" alt="Before" /> <img src="adamcatlace-after.jpg" alt="After" /></div>
这样,我们的JS代码会把它转变如下,然后给滑块添加一个事件,并设置div的宽度:
<div class="image-slider"> <div> <img src="adamcatlace-before.jpg" alt="Before" /> </div> <img src="adamcatlace-after.jpg" alt="After" /> <input type="range" /></div>
JavaScript代码相当简单:
$$('.image-slider').forEach(function(slider) { // Create the extra div and // wrap it around the first image var div = document.createElement('div'); var img = slider.querySelector('img'); slider.insertBefore(img, div); div.appendChild(img); // Create the slider var range = document.createElement('input'); range.type = 'range'; range.oninput = function() { div.style.width = this.value + '%'; }; slider.appendChild(range);});
我们初始的CSS基本和上一个方案的一样。我们只把不需要的部分删掉:
经过这些调整之后我们的CSS代码如下:
.image-slider { position:relative; display: inline-block;}.image-slider > div { position: absolute; top: 0; bottom: 0; left: 0; width: 50%; overflow: hidden;}.image-slider img { display: block; user-select: none;}
使用 input:in-range ,而不仅仅是 input ,这样可以在当且仅当range input被支持的时候才会给range input添加样式。然后你可以使用级联来隐藏它,或针对旧的浏览器给它应用不同的样式。
如果我们测试了这些代码,你会发现它是可运行的,但是它看起来有点糟糕:我们的图片下方随意地放置了一个range input的控件。
我们需要给它应用一些CSS,让它定位在图片上边,并设置宽度和容器宽度一致。
.image-slider input { position: absolute; left: 0; bottom: 10px; width: 100%; margin: 0;}
如下图所示,这看起来终于比较像样了。
还有几个私有伪元素可以给range input添加样式,让它变成我们期望的样子。包括: ::-moz-range-track , ::-ms-track , ::-webkit-slider-thumb , ::-moz-range-thumb , 和 ::-ms-thumb 。像大多数私用特性,它们的结果往往是不一致的、脆弱的、并且无法预测的,所以我建议不要使用它们,除非你真的需要。我就提醒你们一下~
但是,如果我们只是想要在视觉上将range input和控件统一,我们可以使用一个混合模式或滤镜。混合模式 multiply 、 screen 、 luminosity 应该可以生成不错的效果。还有, filter: contrast(4) 可以让滑块变成黑白,小于1的对比值可以让它更偏灰色。可能性是无限的,没有什么通用的最佳选择。你甚至可以混合模式和滤镜一起用,如下:
filter: contrast(.5);mix-blend-mode: luminosity;
我们还可以扩展那块用户使用来调整大小的区域,使之成为更棒的用户体验(根据费茨法则)。可以减小宽度,并结合CSS transform来完成:
width: 50%;transform: scale(2);transform-origin: left bottom;
你可以在图中看到两种处理结果。
这种做法的另一个好处是——尽管只是暂时性的——目前range input有比 resize 属性更好的浏览器支持。