请教一个很怪异的 css3 问题,关于 transform

2020-06-14 21:55:00 +08:00
 myCupOfTea

演示如下

https://codesandbox.io/s/material-progress-scale-qi4zd

可以点击 refresh 演示

背景

最近写上传动画,用了 material-ui 的 progress,后来写上传组件文档的时候,用例里用 requestAnimationFrame 快速刷新进度条,发现进度条虽然 css 属性变了但是没有动

仔细观察发现了,是 translate 在开了过渡(transition)的情况下高速刷新就不会变化,把过渡关了就可以(当然实际场景中不可能刷新这么快,所以我也不可能关了过渡)

但是我脑子一抽试试用 scale 加上 transform-origin 来模拟 translate 呢,神奇的事情出现了

高速刷新状态下,它正常的动了,艹

问题

为啥 scale 可以,但是 translate 不可以

其他问题

用 setTimeout 模拟的时候发现在 120 左右这个延迟,很容易出现进度条抖动(回弹?),就是偶尔会出现进度条一下回去一点然后又前进呢,有办法解决吗(打印过进度 value 呢,是正常的)

1748 次点击
所在节点    程序员
13 条回复
myCupOfTea
2020-06-15 08:42:40 +08:00
好奇,难道没有人遇到过么
liuhuihao
2020-06-15 11:37:31 +08:00
用了 JS requestAnimationFrame 控制进度条进度,为什么还要使用 transition 过渡呢?
myCupOfTea
2020-06-15 12:21:54 +08:00
@liuhuihao requestAnimationFrame 是用来 模拟高速刷新下的 upload 的
myCupOfTea
2020-06-15 12:22:36 +08:00
@liuhuihao 控制进度条还是用的 transition + (translate/scale/width)
redbuck
2020-06-15 16:20:04 +08:00
```html
<style>
.progress {
border: 1px solid #e5e5e5;
margin: 10px;
overflow: hidden;
}

.inner {
transition: transform 0.16s ease;
height: 5px;
background-color: yellowgreen;
transform-origin: 0 50%;
}
</style>
<div class="progress translate">
<div class="inner"></div>
</div>
<div class="progress scale">
<div class="inner"></div>
</div>
<script type="text/javascript">
const sleep = time => new Promise(resolve => setTimeout(resolve, time));
const frame = () => new Promise(resolve => requestAnimationFrame(resolve)) || sleep(16.7);
const $ = s => document.querySelector(s);

let progress = 0;
const translate = $(".translate .inner");
const scale = $(".scale .inner");

function render() {
translate.style.transform = `translateX(${progress - 100}%)`;
scale.style.transform = `scaleX(${progress / 100})`;
}

async function update() {
while (progress <= 100) {
render();
// await frame()
await sleep(50);
progress++;
}
}

update();
</script>
```

帮你改个简单版的,排除多余变量.

问题依然存在.

但如果调整 sleep 的时间,可以发现时间越长,会稍微缓解一点.
gaoryrt
2020-06-15 17:49:13 +08:00
有没有可能是这样的:
两个 fill 上的 transition 进行的都是 transform 默认值到当前值的补帧,而不是上一个值到当前值的补帧。
如果 dom 样式的更新时间足够大,transition duration 足够小,你应该能看到每次从头开始到当前值的动画,而非从上一个值到当前值的动画。
myCupOfTea
2020-06-15 17:59:19 +08:00
@redbuck 是的,这个问题我和 material-ui 官方讨论了下,这种场景下关闭过渡就行了
myCupOfTea
2020-06-15 18:00:48 +08:00
@gaoryrt 嗯,确实刷新时间变慢了就可以呢,
我又另外试了一个属性 width 控制,translate 控制都不行
但是我很好奇 scale 也是加了过渡效果的,但是 scale 是可以的
myCupOfTea
2020-06-15 18:01:30 +08:00
@redbuck 其实我就是好奇 scale 也是加了过渡效果的,但是 scale 是可以的
本来这个场景其实基本不会出现来,没有上传会一直刷新这么快的
myCupOfTea
2020-06-15 18:03:21 +08:00
另外我测试发现 firefox 对 css var 支持不够好(该测试用例在 firefox 上闪烁的很厉害)
我把 css var 去了写了另外一个例子,防止有人觉得是 css var 的问题
https://codesandbox.io/s/material-progress-scale-not-css-var-zw5nt
myCupOfTea
2020-06-15 18:17:12 +08:00
@redbuck
@gaoryrt 其实这个用例在火狐上是可以的,应该是 chromium 的问题
gaoryrt
2020-06-15 18:21:24 +08:00
那应该是 scale 会有类似于视觉残留的效果?
或者说是 react 针对 style 里面的 calc 进行了优化导致了问题?
myCupOfTea
2020-06-16 00:22:04 +08:00
@gaoryrt 跟 react 没关系,我仔细观察过了。css 上的属性已经变了,说明已经从虚拟 dom 渲染到真实 dom 呢,上面也有个老哥写了原生的 js,应该就是 chromium 的问题

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/681513

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX