响应式 SVG 图像叠加层
每次《深海迷航:零度之下》更新,我们都会创建一个微型网站,以清晰的视觉方式详细介绍游戏的新增内容。每次更新的目标都是展示我们在最新抢先体验版更新中添加或改进了哪些内容。信息需要以视觉化、清晰且富有表现力的方式呈现。因此,在构建每个微型网站之前,我会仔细研究相关信息,并构思如何以最佳方式展现每一项新增内容。多年来,我的设计技能不断提升,每个网站都让我有机会精进自己的网页开发技能。
这次更新,我想用一张图片来指出游戏特定区域的新增内容。我不想反复裁剪同一张图片,但也不想仅仅用文字描述需要玩家注意的地方。怎么办呢?这时,响应式SVG就派上用场了。
我知道你在问:“既然PNG也能达到类似的效果,为什么还要用SVG呢?”问得好。首先,我不想影响图像本身。如果用户想把图片保存到其他页面,我希望他们能保存一个干净的版本。其次,PNG在网页上非常消耗资源,尤其是在这种尺寸下(我们需要两倍于预期尺寸的图片才能在Retina显示屏上清晰显示)。第三,SVG是基于矢量的,因此可以完美缩放。它是网站图形最无损的格式。最后,如果需要,我可以响应式地移动路径端点,使其与文本框对齐。
好的,代码呢?
首先,我知道我需要让图片在父元素内实现响应式布局,并且父元素仅受 max-width 属性的限制。使用 SVG 时,必须将包含元素视为一个画布,并在其中进行创作。我需要一个始终保持响应式图片大小的画布:
<style>
#greenhouse {
position: relative;
width: 100%;
vertical-align: middle;
margin: 0;
overflow: hidden;
}
#greenhouse svg {
display: inline-block;
position: absolute;
top: 0;
left: 0;
}
</style>
<div id="greenhouse">
<svg viewBox="0 0 1280 720"
preserveAspectRatio="xMinYMin meet">
<image width="1280"
height="720" xlink:href="https://dkli3tbfz4zj3.cloudfront.net/all/202006_SaladDays/images/greenhouse.jpg">
</image>
</svg>
</div>
这里的关键在于,我们将图像包含在一个 SVG 中viewBox,该 SVG 描述了被包含元素的空间(即我们的画布)。通过将父元素设置为 div position: relative;,子元素设置为 SVG,position: absolute;我们要求 DOM 将 SVG 画布覆盖在父元素之上,#greenhouse并设置宽度指令使其填充父元素的 100 %。
另一个重要的部分是preserveAspectRatio属性,它要求 DOM 计算 SVG 元素在任何尺寸下的特定比例。在本例中,我们要求使用 `--uniform scale` 进行统一缩放xMinYMin,并使用 `--container view box smaller than the viewport` 使视图框小于视口meet。
您还可以看到,我们已明确设置了元素的高度和宽度,<image>使其与视图框相匹配。这一点很重要,因为它确保它们能够协同响应。
下一步是在同一个视图框内绘制我们想要的 SVG 元素。
<div id="greenhouse">
<svg viewBox="0 0 1280 720"
preserveAspectRatio="xMinYMin meet">
<image width="1280"
height="720" xlink:href="https://dkli3tbfz4zj3.cloudfront.net/all/202006_SaladDays/images/greenhouse.jpg">
</image>
<circle cx="220" cy="320" r="40" stroke="#d4ffde"
stroke-width="3" fill="none" />
<circle cx="320" cy="520" r="70" stroke="#d4ffde"
stroke-width="3" fill="none" />
<circle cx="750" cy="380" r="60" stroke="#d4ffde"
stroke-width="3" fill="none" />
</svg>
</div>
我选择使用无填充、描边宽度为 3 像素的圆形。本文篇幅有限,无法详细解释如何绘制 SVG,网上有很多关于各种形状的资料可供参考。我参考了Chris Coyer 的《SVG 路径语法:图解指南》来设计后面会提到的路径。
使用该元素,我们用`x` 表示 x 轴,用`y` 表示 y 轴,<circle>来描述视图框内的坐标。由于这个圆形元素位于同一个父级 SVG 中,因此图像和圆形都使用同一个视图框。瞧,圆形可以完美地随图像缩放。如果需要,我也可以以完全相同的方式放置一个锚点标签,使圆形与锚点对齐。cxcy
但是我们如何在视图框之外绘制 SVG 呢?
很高兴你问到这个问题。我想在图片下方添加描述性文本框,解释每个圆圈里的内容。所以我需要一种方法将 SVG 图形扩展到这个视图框之外。解决方案是什么?在这个视图框上方放置另一个视图框,并提高它的层级z-index。这样,每个人都能使用视图框了!
<style>
#lines {
z-index: 3;
}
</style>
<svg id="lines" viewBox="0 0 1280 1280"
preserveAspectRatio="xMinYMin meet">
<path stroke="#d4ffde" stroke-width="3" fill="none" d="
M 130,820
L 130,320
l 50,0
"/>
<path stroke="#d4ffde" stroke-width="3" fill="none" d="
M 620,820
L 620,520
l -230,0
"/>
<path stroke="#d4ffde" stroke-width="3" fill="none" d="
M 1100,820
L 1100,380
l -290,0
"/>
</svg>
所以我添加了一个新的 SVG 元素,并将其视图框的高度定义为图像视图框高度的两倍。我不会用到全部空间,但这样设置宽高比很容易理解。
我按照前面提到的指南,仔细地从每个圆绘制路径,并将它们锚定在图像及其 SVG 视图框的下方。
当然,我们还会添加 CSS 代码z-index,让这个元素置顶。
最后,我在下面添加了一些描述性文字框。整体效果如下:
<style>
#greenhouse {
position: relative;
width: 100%;
vertical-align: middle;
margin: 0;
overflow: hidden;
}
#greenhouse svg {
display: inline-block;
position: absolute;
top: 0;
left: 0;
}
#lines {
z-index: 3;
}
#greenhouse-details {
display: flex;
margin-top: 60%;
flex-wrap: wrap;
justify-content: center;
}
.narrow-text {
background-color: #d4ffde;
padding: 3% 5%;
margin: 3%;
flex-basis: 25%;
}
@media only screen and (max-width : 480px) {
.narrow-text {
flex-basis: 100%;
}
}
</style>
<div id="greenhouse">
<svg id="lines" viewBox="0 0 1280 1280"
preserveAspectRatio="xMinYMin meet">
<path stroke="#d4ffde" stroke-width="3" fill="none" d="
M 130,820
L 130,320
l 50,0
"/>
<path stroke="#d4ffde" stroke-width="3" fill="none" d="
M 620,820
L 620,520
l -230,0
"/>
<path stroke="#d4ffde" stroke-width="3" fill="none" d="
M 1100,820
L 1100,380
l -290,0
"/>
</svg>
<svg viewBox="0 0 1280 720"
preserveAspectRatio="xMinYMin meet">
<image width="1280"
height="720" xlink:href="https://dkli3tbfz4zj3.cloudfront.net/all/202006_SaladDays/images/greenhouse.jpg">
</image>
<circle cx="220" cy="320" r="40" stroke="#d4ffde"
stroke-width="3" fill="none" />
<circle cx="320" cy="520" r="70" stroke="#d4ffde"
stroke-width="3" fill="none" />
<circle cx="750" cy="380" r="60" stroke="#d4ffde"
stroke-width="3" fill="none" />
</svg>
<div id="greenhouse-details">
<div class="narrow-text">
<p>Whip up a delicious salad</p>
</div>
<div class="narrow-text">
<p>Cuddle up to Marguerit’s Snow Stalker companion</p>
</div>
<div class="narrow-text">
<p>Learn how to farm new, unusual plants</p>
</div>
</div>
</div>
瞧!响应式 SVG 圆形叠加在图像上,其中一些 SVG 路径延伸到图像之外。
临别赠言:
- 文本框的颜色与路径颜色完美匹配。因此,你根本看不出路径实际上位于文本框上方。仔细计算后,最好能确定文本框与路径的交点,并将路径的终点精确地设在那里。由于我们使用的是固定比例,这比听起来要简单得多。
- 我总是建议父元素(图中未显示)具有一个
max-width属性,以防止在超宽屏幕上出现奇怪的现象。 - 这些圆圈原本可以和线条放在同一个 SVG 视图框内。只是由于处理方式的原因,它们在这个例子中没有放在同一个视图框内。
- 在移动设备上,你还需要更进一步,计算当文本框宽度变为 100% 时,路径的终点分别与每个文本框的交点。为了简洁起见,我就把这个挑战留给各位读者自行解决吧。
感谢阅读我的第一篇 dev.to 文章!
文章来源:https://dev.to/damjess/responsive-svg-image-overlays-4bni
