You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Codepen交互式SVG蒙版嵌入540px高全屏宽容器?

解决方案:固定高度容器内嵌入SVG蒙版并保持宽高比

我明白你想要的效果:把这个交互式SVG蒙版放进一个固定高度540px的容器,容器宽度铺满浏览器边缘,同时SVG始终保持原有的16:9宽高比,还要保留所有交互功能。下面是调整后的完整代码和关键改动说明:

修改后的完整代码

HTML

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="svg-container">
  <svg id="demo" xmlns="http://www.w3.org/2000/svg" x="0" y="0" width="1600" height="900" viewBox="0 0 1600 900">
    <defs>
      <radialGradient id="maskGradient">
        <stop offset="50%" stop-color="#fff"/>
        <stop offset="100%" stop-color="#000"/>
      </radialGradient>
      <mask id="theMask">
        <circle id="masker" r="150" fill="url(#maskGradient)" cx="800" cy="450" />
      </mask>
    </defs>
    <image id="lines" xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/314556/roomSketch.jpg" x="0" y="0" width="1600" height="900" />
    <g id="maskReveal" mask="url(#theMask)" >
      <image id="regular" xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/314556/roomColor.jpg" x="0" y="0" width="1600" height="900" />
    </g>
    <circle id="ring" r="20" fill="none" stroke="#dc143c" stroke-width="2" cx="800" cy="450" />
    <circle id="dot" r="4" fill="#dc143c" cx="800" cy="450" />
  </svg>
  <div id="instructions">
    <svg id="dial" xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
      <path id="progressRing" d="M50,10A40,40,0,1,1,10,50,40,40,0,0,1,50,10Z" fill="none" stroke="#fff" stroke-miterlimit="10" stroke-width="6"/>
      <circle r="43" fill="none" stroke="#fff" cx="50" cy="50" stroke-width="2" opacity="0.5" />
      <circle r="37" fill="none" stroke="#fff" cx="50" cy="50" stroke-width="2" opacity="0.5"/>
      <text transform="translate(55 56)" text-anchor="start" font-size="20" fill="#fff">%</text>
      <text id="counter" transform="translate(55 56)" text-anchor="end" font-size="20" fill="#fff">0</text>
    </svg>
    <p>Hover mouse to move mask around.</p>
    <p>Hold & release mouse button to expand & contract mask.</p>
  </div>
</div>

CSS

body {
  padding: 0;
  margin: 0;
  font-family: "Signika", sans-serif;
  background: #262626;
  width: 100%;
  color: white;
}

p {
  margin: 0;
  text-align: center;
  white-space: nowrap;
}

* {
  box-sizing: border-box;
}

/* 新增的容器样式,固定高度540px,宽度铺满浏览器 */
#svg-container {
  height: 540px;
  width: 100%;
  position: relative;
  overflow: hidden; /* 隐藏SVG超出容器的部分 */
  background: #262626;
}

#demo {
  cursor: none;
  position: absolute;
  top: 50%; /* 垂直居中基准 */
}

#instructions {
  position: absolute;
  padding: 12px;
  bottom: 20px;
  background: rgba(0, 0, 0, 0.75);
  left: 50%;
  cursor: none;
  padding-top: 100px;
  user-select: none;
  border-radius: 4px;
  transform: translateX(-50%); /* 水平居中 */
}

#dial {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%); /* 水平居中 */
}

JavaScript

console.clear();
var svg = document.querySelector("#demo");
var tl = new TimelineMax({onUpdate:onUpdate});
var pt = svg.createSVGPoint();
var data = document.querySelector(".tlProgress");
var counter = document.querySelector("#counter");
var ratio = 9/16; // 原SVG是16:9,这里用高度/宽度的比例
var container = document.querySelector("#svg-container"); // 获取容器元素

TweenMax.set("#instructions, #dial", {xPercent: -50});
TweenMax.set("#progressRing", {drawSVG:0});
tl.to("#masker", 2, {attr:{r:2400}, ease:Power2.easeIn});
tl.reversed(true);

function mouseHandler() {
  tl.reversed(!tl.reversed());
}

function getPoint(evt){
  pt.x = evt.clientX;
  pt.y = evt.clientY;
  return pt.matrixTransform(svg.getScreenCTM().inverse());
}

function mouseMove(evt) {
  var newPoint = getPoint(evt);
  TweenMax.set("#dot", {attr:{cx:newPoint.x, cy:newPoint.y}});
  TweenMax.to("#ring, #masker", 0.88, {attr:{cx:newPoint.x, cy:newPoint.y}, ease:Power2.easeOut});
}

function onUpdate() {
  var prog = (tl.progress() * 100);
  TweenMax.set("#progressRing", {drawSVG:prog + "%"});
  counter.textContent = prog.toFixed();
}

function newSize() {
  var containerWidth = container.offsetWidth;
  var containerHeight = 540; // 固定容器高度
  var targetWidth, targetHeight;

  // 根据容器宽度计算SVG的尺寸,保持16:9比例
  if (containerWidth > containerHeight * (16/9)) {
    // 容器足够宽,SVG宽度铺满容器,高度按比例计算
    targetWidth = containerWidth;
    targetHeight = containerWidth * ratio;
    // 垂直居中SVG,裁剪上下超出部分
    TweenMax.set("#demo", {
      attr: { width: targetWidth, height: targetHeight },
      x: 0,
      y: -(targetHeight - containerHeight)/2 // 向上平移,让中间区域显示
    });
  } else {
    // 容器较窄,SVG高度铺满容器,宽度按比例计算,水平居中
    targetHeight = containerHeight;
    targetWidth = containerHeight / ratio;
    TweenMax.set("#demo", {
      attr: { width: targetWidth, height: targetHeight },
      x: (containerWidth - targetWidth)/2, // 水平居中
      y: 0
    });
  }
}

window.addEventListener("mousedown", mouseHandler);
window.addEventListener("mouseup", mouseHandler);
window.addEventListener("mousemove", mouseMove);
newSize();
window.addEventListener("resize", newSize);

关键改动说明

  1. 新增容器元素:添加了#svg-container,固定高度540px、宽度100%,并设置overflow:hidden来隐藏SVG超出容器的部分,确保视觉上符合要求。
  2. 调整CSS定位:
    • 将SVG设置为绝对定位,配合JS计算的偏移值实现居中或铺满。
    • 调整#instructions和#dial的定位,确保它们始终在容器底部居中显示。
  3. 重写尺寸计算逻辑:
    • 不再基于窗口尺寸计算,而是以容器的宽高为基准。
    • 分两种情况处理:容器足够宽时,SVG宽度铺满容器,垂直居中裁剪;容器较窄时,SVG高度铺满容器,水平居中显示。
  4. 修正比例变量:将ratio改为9/16(高度/宽度),更直观地计算对应尺寸。

这样修改后,SVG蒙版就能完美嵌入到固定高度540px的容器中,宽度铺满浏览器边缘,同时始终保持16:9的宽高比,所有交互功能也能正常使用。

内容的提问来源于stack exchange,提问作者Thisura De Silva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:55:09