提问人:Fabrício Matté 提问时间:9/2/2013 最后编辑:CommunityFabrício Matté 更新时间:10/28/2017 访问量:8660
以编程方式从 JS 使用 CSS 转换的干净方法?
Clean way to programmatically use CSS transitions from JS?
问:
正如标题所暗示的那样,有没有一种正确的方法来设置一些初始 CSS 属性(或类)并告诉浏览器将它们转换为另一个值?
例如(小提琴):
var el = document.querySelector('div'),
st = el.style;
st.opacity = 0;
st.transition = 'opacity 2s';
st.opacity = 1;
这不会对 Chrome 29/Firefox 23 中元素的不透明度进行动画处理。这是因为(来源):
[...]您会发现,如果应用两组属性,则会立即应用一组属性 在另一个之后,浏览器会尝试优化属性 更改,忽略初始属性并阻止转换。 在幕后,浏览器在绘制之前批量处理属性更改 虽然通常会加快渲染速度,但有时会产生不利影响 影响。
解决方案是在应用两组 性能。执行此操作的一种简单方法是访问 DOM 元素的属性 [...]
offsetHeight
事实上,该黑客确实适用于当前的 Chrome/Firefox 版本。更新了代码(小提琴 - 打开小提琴后单击以再次运行动画):Run
var el = document.querySelector('div'),
st = el.style;
st.opacity = 0;
el.offsetHeight; //force a redraw
st.transition = 'opacity 2s';
st.opacity = 1;
但是,这是相当黑客的,据报道在某些Android设备上不起作用。
另一个答案建议使用,以便浏览器有时间执行重绘,但它也失败了,因为我们不知道重绘需要多长时间才能发生。猜测相当多的毫秒数(30-100?)来确保发生重绘意味着牺牲性能,不必要地空闲,希望浏览器在那一小段时间内执行一些魔术。setTimeout
通过测试,我找到了另一个在最新的 Chrome 上运行良好的解决方案,使用 requestAnimationFrame
(fiddle):
var el = document.querySelector('div'),
st = el.style;
st.opacity = 0;
requestAnimationFrame(function() {
st.transition = 'opacity 2s';
st.opacity = 1;
});
我假设在执行回调之前等到下一次重绘开始之前,因此浏览器不会批量处理属性更改。这里不完全确定,但在 Chrome 29 上运行良好。requestAnimationFrame
更新:经过进一步测试,该方法在 Firefox 23 上效果不佳 - 大多数时候似乎都失败了。(小提琴requestAnimationFrame
)
是否有适当或推荐的(跨浏览器)方法来实现这一目标?
答:
这是一个工作版本。自己看看。
在 Chrome、Firefox、Opera 上测试。
在我的 firefox 版本上,它不支持 style.transition,因此如果标准名称不可用,我会回退到供应商特定名称。
var el = document.querySelector('div');
var VENDORS = ['Moz', 'Webkit', 'Ms', 'O'];
function getVendorSpecificName(el, prop) {
var style = el.style;
if (prop in style) {
return prop;
}
prop = ucfirst(prop);
for (var i = 0, l = VENDORS.length, name; i < l; i++) {
name = VENDORS[i] + prop;
if (name in style) {
return name;
}
}
return null;
}
function ucfirst(str) {
return str && str.charAt(0).toUpperCase() + str.substring(1);
}
function toCamelCase(str) {
return str.split('-').map(function (str, i) {
return i > 0 ? ucfirst(str) : str;
}).join('');
}
function animateCss(el, prop, from, to, duration) {
var style = el.style,
camel = toCamelCase(prop),
vendorSpecific = getVendorSpecificName(el, camel);
if (!vendorSpecific) {
console.log(prop + ' is not supported by this browser');
return false;
}
var transitionPropName = getVendorSpecificName(el, 'transition');
if (!(transitionPropName in style)) {
console.log('transition is not supported by this browser');
return false;
}
style[vendorSpecific] = from;
setTimeout(function () {
style[transitionPropName] = prop + ' ' + duration + 's ease';
setTimeout(function () {
style[vendorSpecific] = to;
}, 1);
}, 1);
return true;
}
animateCss(el, 'opacity', 0, 1, 2);
让我解释一下这是怎么回事:
做了一些辅助函数,比如ucfirst、toCamelCase
- style 属性使用驼峰大小写名称
如果标准名称不可用,请尝试查找供应商特定的样式属性名称
利用setTimeout函数确保浏览器重绘
- 据我所知,所有浏览器总是在超时时重绘
我试图使它更通用的功能,以便也可以应用其他属性,例如颜色或背景。
希望这有帮助!
评论
你不需要太多的 JavaScript 来实现你想要的,只需使用 CSS 关键帧和动画即可获得相同的效果。
div {
opacity: 0;
}
div.fadeIn {
-webkit-animation: fadeIn 2s forwards;
animation: fadeIn 2s forwards;
}
@keyframes fadeIn {
0% {opacity: 0;}
100% {opacity: 1;}
}
@-webkit-keyframes fadeIn {
0% {opacity: 0;}
100% {opacity: 1;}
}
如此 JsFiddle 所示,它要么在页面加载(已添加类)中工作,要么在动态添加类以触发动画时工作。
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script> <script> $(document).ready(function(){ $('a.hiw*').click(function(){ id = this.id; dval = $('#'+id).attr('data-value'); if (dval == 0) { $('a.hiw*').attr('data-value','0'); $( ".hiw-popup" ).remove(); $('#'+id).attr('data-value','1'); $('<div class="hiw-popup white-well run-animation hidden-xs"><div class="row text-center"><div class="col-sm-2 animation-hiw col-xs-2"><span class="glyphicon glyphicon-pencil hiw-icon1" style="background:#ffffff;">1</span><br/>block1</div><div class="col-sm-2 animation-hiw col-xs-2"><span class="glyphicon glyphicon-shopping-cart hiw-icon2">2</span><br/>BLOCK3</div><div class="col-sm-3 animation-hiw col-xs-3"><span class="glyphicon glyphicon-folder-open hiw-icon3">3</span><br/>BLOCK2</div><div class="col-sm-3 animation-hiw col-xs-3"><span class="glyphicon glyphicon-ok hiw-icon4">4</span><br/>BLOCK</div><div class="col-sm-2 animation-hiw col-xs-2"><span class="glyphicon glyphicon-arrow-down hiw-icon5">5</span><br/>BLOCK</div></div></div>').insertAfter('#'+id); }else{ $('#'+id).attr('data-value','0'); $( ".hiw-popup" ).remove(); } }); }); var ahiw = function(id){ dval = $('#'+id).attr('data-value'); if (dval == 0) { $('a.hiw*').attr('data-value','0'); $( ".hiw-popup" ).remove(); $('#'+id).attr('data-value','1'); $('<div class="hiw-popup white-well run-animation hidden-xs"><div class="row text-center"><div class="col-sm-2 animation-hiw col-xs-2"><span class="glyphicon glyphicon-pencil hiw-icon1" style="background:#ffffff;">1</span><br/>block1</div><div class="col-sm-2 animation-hiw col-xs-2"><span class="glyphicon glyphicon-shopping-cart hiw-icon2">2</span><br/>BLOCK3</div><div class="col-sm-3 animation-hiw col-xs-3"><span class="glyphicon glyphicon-folder-open hiw-icon3">3</span><br/>BLOCK2</div><div class="col-sm-3 animation-hiw col-xs-3"><span class="glyphicon glyphicon-ok hiw-icon4">4</span><br/>BLOCK</div><div class="col-sm-2 animation-hiw col-xs-2"><span class="glyphicon glyphicon-arrow-down hiw-icon5">5</span><br/>BLOCK</div></div></div>').insertAfter('#'+id); }else{ $('#'+id).attr('data-value','0'); $( ".hiw-popup" ).remove(); } } </script>
/* Chrome, Safari, Opera */ @-webkit-keyframes animation-hiw-icon { from { background-color: #d9d9d9; } to { background-color: #4ad18f; } } /* Standard syntax */ @keyframes animation-hiw-icon { from { background-color: #d9d9d9; } to { background-color: #4ad18f; } } /* Chrome, Safari, Opera */ @-webkit-keyframes animation-hiw-prog { from { background-color: #d9d9d9; width: 0% } to { width: 100%; background-color: #4ad18f; } } /* Standard syntax */ @keyframes animation-hiw-prog { from { width: 0% } to { width: 100%; } } /* Chrome, Safari, Opera */ @-webkit-keyframes animation-hiw-pop { from { opacity: 0.5; background-color: #d9d9d9; -ms-transform: scale(0.8); /* IE 9 */ -webkit-transform: scale(0.8); /* Chrome, Safari, Opera */ transform: scale(0.8); } to { background-color: #4ad18f; opacity: 1; font-weight: normal; -ms-transform: scale(.8); /* IE 9 */ -webkit-transform: scale(.8); /* Chrome, Safari, Opera */ transform: scale(.8); } } /* Standard syntax */ @keyframes animation-hiw-pop { from { background-color: #d9d9d9; opacity: 0.5; -ms-transform: scale(0.8); /* IE 9 */ -webkit-transform: scale(0.8); /* Chrome, Safari, Opera */ transform: scale(0.8); } to { background-color: #4ad18f; opacity: 1; font-weight: normal; -ms-transform: scale(.8); /* IE 9 */ -webkit-transform: scale(.8); /* Chrome, Safari, Opera */ transform: scale(.8); } } /*Animation Trigger*/ .run-animation .hiw-progress:after, .run-animation .animation-hiw, .run-animation .hiw-icon1, .run-animation .hiw-icon2, .run-animation .hiw-icon3, .run-animation .hiw-icon4, .run-animation .hiw-icon5 { -webkit-animation-play-state: running; /* Safari and Chrome */ animation-play-state: running; } .run-animation .hiw-progress:after, .run-animation .animation-hiw, .run-animation .hiw-icon1, .run-animation .hiw-icon2, .run-animation .hiw-icon3, .run-animation .hiw-icon4, .run-animation .hiw-icon5 { -webkit-animation-play-state: running; animation-play-state: running; } .hiw-progress:after { content: ""; width: 0%; height: 5px; background: #4ad18f; display: inline-block; position: absolute; top: 0; left: 0; -webkit-animation: animation-hiw-prog 5s linear forwards; animation: animation-hiw-prog 5s linear forwards; -webkit-animation-play-state: paused; animation-play-state: paused; } .white-well { background-color: #fff; padding: 10px 15px; border-radius: 5px; border: 1px solid #f1f1f1; } .hiw-popup { position: absolute; width: 100%; z-index: 9; margin: 30px 0 0 -15px; padding: 0px 15px !important; border-color: rgba(0, 0, 0, 0.25) !important; box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.60); -webkit-box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.60); -mz-box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.60); } .hiw-popup .arrow { position: absolute; display: block; width: 0; height: 0; border-color: transparent; border-style: solid; border-width: 11px; left:90%; margin-left: -11px; border-top-width: 0; border-bottom-color: rgba(0, 0, 0, 0.25); top: -11px; } .hiw-popup .glyphicon { margin-bottom: 10px; margin-right: 0px !important;font-weight:bold; background-color: #ffffff;color:#222222 !important; } .white-well .glyphicon { background-color: #ffffff!important; border-radius: 76px;margin-top: -3px;color:#d9d9d9 !important; padding: 5px 9px 8px; color: #fff; box-shadow: 0px 0px 3px #222222; border: 3px solid #ffffff; } .glyphicon { position: relative; top: 1px; display: inline-block; font-family: 'Glyphicons Halflings'; font-style: normal; font-weight: normal; line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .clearfix:before, .clearfix:after, .container:before, .container:after, .container-fluid:before, .container-fluid:after, .row:before, .row:after, .form-horizontal .form-group:before, .form-horizontal .form-group:after, .btn-toolbar:before, .btn-toolbar:after, .btn-group-vertical > .btn-group:before, .btn-group-vertical > .btn-group:after, .nav:before, .nav:after, .navbar:before, .navbar:after, .navbar-header:before, .navbar-header:after, .navbar-collapse:before, .navbar-collapse:after, .modal-footer:before, .modal-footer:after, .review:before, .review:after, .panel-body:before, .panel-body:after { content: " "; display: table; } .animation-hiw:nth-child(1) { -webkit-animation-delay: .2s; animation-delay: .2s; } .hiw-icon5 { -webkit-animation: animation-hiw-icon 0.2s forwards; animation: animation-hiw-icon 0.2s forwards; -webkit-animation-delay: 5s; animation-delay: 5s; -webkit-animation-play-state: paused; animation-play-state: paused; } .hiw-icon4 { -webkit-animation: animation-hiw-icon 0.2s forwards; animation: animation-hiw-icon 0.2s forwards; -webkit-animation-delay: 3.75s; animation-delay: 3.75s; -webkit-animation-play-state: paused; animation-play-state: paused; } .hiw-icon3 { -webkit-animation: animation-hiw-icon 0.2s forwards; animation: animation-hiw-icon 0.2s forwards; -webkit-animation-delay: 2.25s; animation-delay: 2.25s; -webkit-animation-play-state: paused; animation-play-state: paused; } .hiw-icon2 { -webkit-animation: animation-hiw-icon 0.2s forwards; animation: animation-hiw-icon 0.2s forwards; -webkit-animation-delay: 1s; animation-delay: 1s; -webkit-animation-play-state: paused; animation-play-state: paused; } .hiw-icon1 { -webkit-animation: animation-hiw-icon 0.2s forwards; animation: animation-hiw-icon 0.2s forwards; -webkit-animation-delay: .2s; animation-delay: .2s; -webkit-animation-play-state: paused; animation-play-state: paused; } .animation-hiw { -webkit-animation: animation-hiw-pop 0.2s forwards; /* Chrome, Safari, Opera */ animation: animation-hiw-pop 0.2s forwards; -webkit-animation-play-state: paused; /* Safari and Chrome */ animation-play-state: paused; opacity: 0.5; -ms-transform: scale(0.8); /* IE 9 */ -webkit-transform: scale(0.8); /* Chrome, Safari, Opera */ transform: scale(0.8); background: #d9d9d9; width: 15%; padding: 2% 1%; height: 140px; color: #ffffff; float: left; } .animation-hiw:nth-child(1){ -webkit-animation-delay: .2s; animation-delay: .2s; } .animation-hiw:nth-child(2){ -webkit-animation-delay: 1s; animation-delay: 1s; } .animation-hiw:nth-child(3){ -webkit-animation-delay: 2.25s; animation-delay: 2.25s; } .animation-hiw:nth-child(4){ -webkit-animation-delay: 3.75s; animation-delay: 3.75s; } .animation-hiw:nth-child(5){ -webkit-animation-delay: 5s; animation-delay: 5s; } hiw { visibility: hidden; font-size: 12px; font-style: italic; text-align: right; float: right; }
<body> <a href="javascript:void(0);" class="hiw hidden-xs" id="hiw_1" data-value="1" style="float:LEFT;margin-right:10px;color: #4ad18f;font-size: 12px;padding:0px 0px 5px 0px;">How it works</a> </body>
#
目前还没有一种干净的方法(不使用CSS动画 - 有关使用CSS动画的示例,请参阅James Dinsdale的附近答案)。有一个规范错误 14617,不幸的是,自 2011 年提交以来没有采取行动。
setTimeout
在 Firefox 中不能可靠地工作(这是设计使然)。
我不确定——对原始问题的编辑说它也不能可靠地工作,但我没有调查。(更新:看起来至少有一个Firefox核心开发人员认为是你可以进行更多更改的地方,不一定能看到之前更改的效果。requestAnimationFrame
requestAnimationFrame
强制重排(例如通过访问)是一种可能的解决方案,但要使转换正常工作,强制重新设置样式(即 getComputedStyle
)就足够了: https://timtaubert.de/blog/2012/09/css-transitions-for-dynamically-created-dom-elements/offsetHeight
window.getComputedStyle(elem).opacity;
请注意,仅仅运行是不够的,因为它是延迟计算的。我相信无论您从 getComputedStyle 中询问哪个属性,重新设置样式仍然会发生。请注意,请求与几何体相关的属性可能会导致更昂贵的回流焊。getComputedStyle(elem)
有关回流/重新设计/重新绘制的更多信息:http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/
评论
自 2013 年以来,情况发生了变化,所以这里有一个新的答案:
您可以使用 Web 动画。它们是在 Chrome 36 和 Firefox 40 中原生实现的,并且所有其他浏览器都有一个 polyfill。
示例代码:
var player = snowFlake.animate([
{transform: 'translate(' + snowLeft + 'px, -100%)'},
{transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);
// less than 1500ms later...changed my mind
player.cancel();
评论
opacity:0
opacity:1
setTimeout
requestAnimationFrame
requestAnimationFrame
setTimeout
requestAnimationFrame
setTimeout
opacity:1
opacity:0
transition
transition