利用var()实现DOM元素放大缩小的方案

862 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情

前言

当用户需要控制页面列表上的元素大小时,应该怎么处理?

第一反应都是使用 transform: scale(); 就能实现元素的放大缩小,但是这种改变并不会改变元素本身占用的位置,换句话说,这种方法使得元素变大,也不会挤开其他元素;就算变小了,也还是占用那么多位置,其他元素也不会靠拢。

还有一种就是使用 zoom,它也可以使元素放大缩小,同时也会改变元素所占用的位置大小,解决了 transform: scale(); 的痛点,但可惜的是,这并不是一个标准的功能,使用风险很大,不建议使用(详见)。

于是,本人借用 CSSvar() 函数,手动实现了一种方案。

效果

基本思路

父级元素上,通过 style 定义 css变量,记录当前子元素的宽高信息,在子元素的 css 中,通过 var() 函数获取 优先级最高 的变量,用于设置自身的宽高,从而实现控制元素大小改变的效果。

因为这种设置并不是真的实现缩放,所以子元素本身的内容最好使用百分比、**flex**等等相对于父级的单位来或者响应式布局来控制,这样每当子元素的宽高发生变化时,子元素的内容也会随之改变,从而实现缩放的效果。

下面实现一个简单的例子。

代码

HTML 布局

页面主要有两个大部分,一个是展示内容的元素列表,一个是用户用于控制列表中元素大小的滑块:

<div id="app">
  <!-- 滑块 -->
  <input id="range" type="range" name="points" min="1" max="10" value="2">
  <!-- 列表 -->
  <div class="list">
    <!-- .card 为列表元素 -->
    <div class="card">
      <img src="https://tse1-mm.cn.bing.net/th/id/OIP-C.SFXnRiI3y-8vGyVwlpeh2gHaLR?pid=ImgDet&rs=1" />
    </div>
    <div class="card">
      <img src="https://tse2-mm.cn.bing.net/th/id/OIP-C.ctLV7v4WRjZLkZeJ80HE1QHaJ5?pid=ImgDet&rs=1" />
    </div>
    <div class="card">
      <img src="https://tse1-mm.cn.bing.net/th/id/OIP-C.Fu9VRpgwfyDm9ucWZNVqUwHaK1?pid=ImgDet&rs=1" />
    </div>
    <div class="card">
      <img src="https://tse3-mm.cn.bing.net/th/id/OIP-C.SLpJtyB5BbR5G0H0TPbhvwHaLK?pid=ImgDet&rs=1" />
    </div>
  </div>
</div>

然后再加上一点样式,重点注意列表的子元素 .card 的大小设置:

#app {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background-color: bisque;
}

.list {
  width: 100%;
  padding: 20px;
}

.card {
  display: inline-block;
  /* 这里是重点,使用 var() 获取元素的宽高 */
  width: var(--width);
  height: var(--height);
  background-color: palegoldenrod;
  margin: 5px;
  padding: 5px;
}

/* .card 里面的内容,使用 相对于父级的百分比单位进行控制 */
img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

微信截图_20220812114755.png 基本上的页面效果就已经出来了。

接下来就是通过 JS 获取用户的拖动结果,将其转换为倍率,然后计算出列表的子元素的宽高信息,通过 style 设置 css变量,核心代码如下:

注意:这里使用的 TS 。

// 获取滑块
const range: HTMLInputElement = document.querySelector('#range')
// 监听滑块的值的变动
range.oninput = function() {
  // 根据滑块的值计算出 缩放比率
  const zoom = +range.value * 0.5
  // 根据缩放比率计算出子元素的宽高,然后设置
  setInfo({
    width: cardBaseInfo.width * zoom,
    height: cardBaseInfo.height * zoom
  })
}

// 设置宽高信息
function setInfo(info: CardBaseInfo): void {
  // 根据优先级,这里最好获取列表元素,此处为了方便,就获取全局的 app 元素
  const app: HTMLDivElement = document.querySelector('#app')
  if (!app) return
  // 在 style 上设置变量,记得加上单位
  app.style.cssText = `--width:${info.width}px;--height:${info.height}px;`
}

到这里位置,基本整个方案就完成了,详细代码见码上掘金