Отличие от попиксельной «HTML-анимации» заключается в том, что «CSS-анимации» нужен только Javascript для установки точки, в которой CSS-переход заботится о промежуточных перемещениях. , Динамическая установка нескольких точек является альтернативой CSS-анимации , но имеет гораздо больший контроль над кодом и взаимодействием.
У меня есть следующий код, где я хочу, чтобы drag-zoom имел плавный переход CSS.
<html>
<head>
</head>
<body>
<div id="waterfall"></div>
<script>
////////////////////////////////////////////////////////////////////////////////////
// draw
var fileData, max_zoom_view_value;
var view_A, view_B, exampleSize = 3;
var ratio_A, ratio_B;
function rangePercent(a, b) {
return 100*(b - a)/(max_zoom_view_value)
}
function simulate_data() {
fileData = ["foo.js", "bar.js", "style.css", "image.png", "script.js", "my_mod.js", "img_123.jpg"];
for (var i = 0; i < exampleSize; i++) fileData = fileData.concat(fileData); //make example data bigger
max_zoom_view_value = 0
fileData.forEach((x, i) => { //add more data
var a = i*100, b = i*100 + 100; //simulate bars start and stop positions
fileData[i] = {key: fileData[i], start: a, stop: b}
if (b > max_zoom_view_value) max_zoom_view_value = b
})
ratio_A = 0.001; ratio_B = 1;
}
//factor ratio fraction range view
function draw() {
var htm = "<table cellpadding='5'><tr style = 'text-align:center; font-weight:bold;'><td>file</td><td>type</td><td>time</td><td id='zoomZone'>waterfall</td></tr>";
var zoomSelectField = "<div id='zoomField' style='position:absolute; height:100%; background:gray; opacity:.2;'></div>";
view_A = ratio_A * max_zoom_view_value
view_B = ratio_B * max_zoom_view_value
max_zoom_view_value = view_B - view_A //zoomed in
for (let i = 0; i < fileData.length; i++) {
var x = fileData[i], name = x.key, type = name.split('.')[1];
var start = x.start, stop = x.stop;
///////////
/* ............view............
A B
: :
...][cut| ][...bars in view...][ |cut]...
: : : :
start stop start stop
*/
var A = start, B = stop; //bar positions in view
if (start < view_A) { A = view_A } //cut
if (stop > view_B) { B = view_B } //cut
if (stop < view_A || start > view_B) A = B = 0; //erase bars outside view
////////////
var barAlign = rangePercent(0, A - view_A), barWidth = rangePercent(A, B);
htm += "<tr><td>" + name + "</td><td>" + type + "</td><td>" + parseInt(stop - start) + " ms</td>";
htm += "<td id='id" + i + "' style='transition: width 2s; cursor:pointer; width:100%; overflow:hidden;'>";
htm += "<div class='bar' style='margin-left:" + barAlign + "%; width:" + barWidth + "%;'> <sub>" + parseInt(start) + " - " + parseInt(stop) + "</sub></div></td></tr>";
}
document.querySelector('#waterfall').innerHTML = "<div class='waterfall noselect' onmousemove='mousemove(event)'>" + zoomSelectField + htm + "</table></div>"
// https://jsperf.com/fragments-vs-html-strings
// https://jsperf.com/document-fragment-test-peluchetti
zoomZone = document.querySelector('#zoomZone')
zoomField = document.querySelector('#zoomField')
}
function draw2() {
for (let i = 0; i < fileData.length; i++) {
var x = document.querySelector('#id' + i)
x.style.width = "400px"
}
}
setTimeout(draw2, 5000)
////////////////////////////////////////////////////////////////////////////////////
var style = document.createElement('style');
style.innerHTML = "*{font-family:arial, sans-serif;} .waterfall{position: relative;} .noselect {-webkit-touch-callout: none;-webkit-user-select: none;-khtml-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;} .bar{display:inline-block; background-color: lightGray;} table{border-collapse:collapse; white-space:nowrap;} td{border: solid 1px gray} ";
document.head.appendChild(style);
////////////////////////////////////////////////////////////////////////////////////
// zoom
var zoomZone, zoomField, mouseX, fixedX, bod = document.body, mouseDown = 0;
function mousemove(e) {
mouseX = e.clientX;
var w = mouseDown? Math.abs(mouseX - fixedX): 1
zoomField.style.width = w + 'px'
zoomField.style.left = (mouseX > fixedX? mouseX - w: mouseX) + 'px'
if (mouseX < zoomZone.offsetLeft) redraw()
}
bod.onmousedown = select
bod.onmouseup = startZoom
document.onmouseleave = redraw
function select() {
mouseDown = 1;
fixedX = mouseX
}
function redraw() {
cancel()
simulate_data()
draw()
}
function cancel() {
mouseDown = 0;
zoomField.style.width = '0px'
}
////////////////////////////////////////////////////////////////////////////////////
// CSS transition
function startZoom(e) {
if (e.clientX == fixedX) redraw()
else {
var A = mouseX - zoomZone.offsetLeft, B = fixedX - zoomZone.offsetLeft
if (A > B) { var swap = A; A = B; B = swap; }
ratio_A = A/zoomZone.offsetWidth
ratio_B = B/zoomZone.offsetWidth
cancel()
draw()
}
}
simulate_data()
draw()
</script>
</body>
</html>
Итак, первое, что я пытаюсь сделать, - это минимальное «доказательство концепции»:
- Найдите рабочий фрагмент и скопируйте
transition: width 2s;
- Вставьте его в
<td id='id" + i + "' style='transition: width 2s; ...
. Также сделан уникальный идентификатор для изменения ширины позже на document.querySelector('#id' + i).style.width = "400px"
, через пять секунд. Смотрите код.
- Запустите фрагмент кода и подождите 5 секунд. Переход не гладкий!
Как я могу заставить CSS переход работать? Или CSS анимация? Какие есть альтернативы? Ширина должна быть установлена динамически, и 400 - только пример. Я думаю, что простая анимация слишком медленная.
Обновление
Как указал @GramThanos, он работает, когда пиксели и процент не смешаны, как в неизменном фрагменте выше. Изменение "400px" на "50%" будет плавно уменьшать масштаб. Но увеличение на «200%» не сработало!
Обновление 2
A <td>
невозможно увеличить на «200%»! Переместите его во внутренний <div>
, и он работает. overflow:hidden
затем режет то, что выходит за пределы <td>
.
Название изменено на более общее, поскольку в общем случае при любых перемещениях и свойствах для анимации это используемое решение также может работать:
<div id='id" + i + "' style='width:100%; transition: width 2s, margin-left 2s;'>
width
и margin-left
вы меняете на то, что вам нужно. Несколько свойств для анимации разделяются запятой. Можно установить другие начальные значения, чем по умолчанию. Например, здесь width:100%
, чтобы быть уверенным, и margin-left
уже 0
и не должны упоминаться в качестве начального значения.
Требуется установка уникального идентификатора для каждого элемента, чтобы иметь Javascript для изменения стиля элементов, например:
setTimeout(draw2,4000)
function draw2() {
for (let i = 0; i < fileData.length; i++) {
var x = document.querySelector('#id' + i)
x.style.width = "200%"
x.style.marginLeft = "-50%"
}
}
Обратите внимание, что в JS любой -
должен быть удален, поэтому margin-left
должен быть записан marginLeft
. Здесь масштаб увеличивается одинаково влево и вправо на каждом #id. Чтобы получить нужные ходы, может понадобиться ручка и бумага, а также некоторые расчеты.
Вот полный код:
<html>
<head>
</head>
<body>
<div id="waterfall"></div>
<script>
////////////////////////////////////////////////////////////////////////////////////
// draw
var fileData, max_zoom_view_value;
var view_A, view_B, exampleSize = 3;
var ratio_A, ratio_B;
function rangePercent(a, b) {
return 100*(b - a)/(max_zoom_view_value)
}
function simulate_data() {
fileData = ["foo.js", "bar.js", "style.css", "image.png", "script.js", "my_mod.js", "img_123.jpg"];
for (var i = 0; i < exampleSize; i++) fileData = fileData.concat(fileData); //make example data bigger
max_zoom_view_value = 0
fileData.forEach((x, i) => { //add more data
var a = i*100, b = i*100 + 100; //simulate bars start and stop positions
fileData[i] = {key: fileData[i], start: a, stop: b}
if (b > max_zoom_view_value) max_zoom_view_value = b
})
ratio_A = 0.001; ratio_B = 1;
}
//factor ratio fraction range view
function draw() {
var htm = "<table cellpadding='5'><tr style = 'text-align:center; font-weight:bold;'><td>file</td><td>type</td><td>time</td><td id='zoomZone'>waterfall</td></tr>";
var zoomSelectField = "<div id='zoomField' style='position:absolute; height:100%; background:gray; opacity:.2;'></div>";
view_A = ratio_A * max_zoom_view_value
view_B = ratio_B * max_zoom_view_value
max_zoom_view_value = view_B - view_A //zoomed in
for (let i = 0; i < fileData.length; i++) {
var x = fileData[i], name = x.key, type = name.split('.')[1];
var start = x.start, stop = x.stop;
///////////
/* ............view............
A B
: :
...][cut| ][...bars in view...][ |cut]...
: : : :
start stop start stop
*/
var A = start, B = stop; //bar positions in view
if (start < view_A) { A = view_A } //cut
if (stop > view_B) { B = view_B } //cut
if (stop < view_A || start > view_B) A = B = 0; //erase bars outside view
////////////
var barAlign = rangePercent(0, A - view_A), barWidth = rangePercent(A, B);
htm += "<tr><td>" + name + "</td><td>" + type + "</td><td>" + parseInt(stop - start) + " ms</td>";
htm += "<td style='cursor:pointer; width:100%; overflow:hidden;'><div id='id" + i + "' style='width:100%; transition: width 2s, margin-left 2s;'>";
htm += "<div class='bar' style='margin-left:" + barAlign + "%; width:" + barWidth + "%;'> <sub>" + parseInt(start) + " - " + parseInt(stop) + "</sub></div></div></td></tr>";
}
document.querySelector('#waterfall').innerHTML = "<div class='waterfall noselect' onmousemove='mousemove(event)'>" + zoomSelectField + htm + "</table></div>"
// https://jsperf.com/fragments-vs-html-strings
// https://jsperf.com/document-fragment-test-peluchetti
zoomZone = document.querySelector('#zoomZone')
zoomField = document.querySelector('#zoomField')
}
function draw2() {
for (let i = 0; i < fileData.length; i++) {
var x = document.querySelector('#id' + i)
x.style.width = "200%"
x.style.marginLeft = "-50%"
}
}
setTimeout(draw2,4000)
////////////////////////////////////////////////////////////////////////////////////
var style = document.createElement('style');
style.innerHTML = "*{font-family:arial, sans-serif;} .waterfall{position: relative;} .noselect {-webkit-touch-callout: none;-webkit-user-select: none;-khtml-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;} .bar{display:inline-block; background-color: lightGray;} table{border-collapse:collapse; white-space:nowrap;} td{border: solid 1px gray} ";
document.head.appendChild(style);
////////////////////////////////////////////////////////////////////////////////////
// zoom
var zoomZone, zoomField, mouseX, fixedX, bod = document.body, mouseDown = 0;
function mousemove(e) {
mouseX = e.clientX;
var w = mouseDown? Math.abs(mouseX - fixedX): 1
zoomField.style.width = w + 'px'
zoomField.style.left = (mouseX > fixedX? mouseX - w: mouseX) + 'px'
if (mouseX < zoomZone.offsetLeft) redraw()
}
bod.onmousedown = select
bod.onmouseup = startZoom
document.onmouseleave = redraw
function select() {
mouseDown = 1;
fixedX = mouseX
}
function redraw() {
cancel()
simulate_data()
draw()
}
function cancel() {
mouseDown = 0;
zoomField.style.width = '0px'
}
////////////////////////////////////////////////////////////////////////////////////
// CSS transition
function startZoom(e) {
if (e.clientX == fixedX) redraw()
else {
var A = mouseX - zoomZone.offsetLeft, B = fixedX - zoomZone.offsetLeft
if (A > B) { var swap = A; A = B; B = swap; }
ratio_A = A/zoomZone.offsetWidth
ratio_B = B/zoomZone.offsetWidth
cancel()
draw()
}
}
simulate_data()
draw()
</script>
</body>
</html>