携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情
前言
当用户需要控制页面列表上的元素大小时,应该怎么处理?
第一反应都是使用 transform: scale(); 就能实现元素的放大缩小,但是这种改变并不会改变元素本身占用的位置,换句话说,这种方法使得元素变大,也不会挤开其他元素;就算变小了,也还是占用那么多位置,其他元素也不会靠拢。
还有一种就是使用 zoom,它也可以使元素放大缩小,同时也会改变元素所占用的位置大小,解决了 transform: scale(); 的痛点,但可惜的是,这并不是一个标准的功能,使用风险很大,不建议使用(详见)。
于是,本人借用 CSS 中 var() 函数,手动实现了一种方案。
效果
基本思路
在父级元素上,通过 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;
}
基本上的页面效果就已经出来了。
接下来就是通过 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;`
}
到这里位置,基本整个方案就完成了,详细代码见码上掘金。