#javascript #html #reactjs
#javascript #HTML #reactjs
Вопрос:
У меня есть диалоговое окно PDFViewer в моем веб-приложении, созданном в React, в нем отображаются PDF-файлы с использованием этого пакета: https://github.com/wojtekmaj/react-pdf , он имеет свойство scale для определения масштаба компонента просмотра PDF как такового:
<Page pageNumber={pageNumber} scale={scale} height={props.height} />
Свойство scale сохраняется в состоянии и изменяется элементами управления на странице. Он работает правильно, единственная проблема заключается в том, что он не анимирует переход при изменении масштаба. Каков правильный способ плавной настройки состояния шкалы?
Ответ №1:
Я не нашел такой опции в их github, так что вам нужно будет реализовать ее самостоятельно.
Вам понадобится:
- Два значения состояния — одно для целевого масштаба и одно для текущего масштаба.
- Знание
setTimeout
- Немного арифметики
Простой и очень медленный пример:
// initially we set both states to same value
let [target_scale, set_target_scale] = React.useState(1);
let [current_scale, set_current_scale] = React.useState(1);
let [step, set_step] = React.useState(0);
// we need to get our step. We calculate it when target changes
React.useEffect(
function () {
// step is amount that we want to animate on each frame
// 1000 is animation duration (in this case it is one second), and 16 is amount of ms to get 60fps
// 1000 / 16 will give us amount of steps to perform animation. In this case it would be 62.5 frames. Math floor wil make it 62 frames
// Note that if current_scale is larger then target scale, we will get negative value
set_step((target_scale - current_scale) / Math.ceil(1000 / 16));
},
[target_scale]
);
// Now we need and effect that will look for state changes and run our animation
React.useEffect(
function () {
// we don't need it to run if both scales are the same
// because all numbers in JS are doubles, we want to round it up, to not miss this point
if (current_scale.toFixed(2) === target_scale.toFixed(2)) return;
let timeout_id = window.setTimeout(
() => set_current_scale(current_scale step),
16
);
return () => window.clearTimeout(timeout_id);
},
[target_scale, current_scale, step]
);
Вот рабочий пример:
https://jsfiddle.net/136wjope/1/
Удивительная часть здесь в том, что React значительно оптимизирует этот материал. Плохая часть в том, что это грязно и, вероятно, глючит. Также здесь нет приятного смягчения. Но, по крайней мере, вы знаете, как это сделать, что вам нужно. Итак, чтобы не тратить рабочее время впустую, я предлагаю вам использовать библиотеку react-spring. Это основано на скорости, и это делает ваши анимации приятными. Также он может анимировать произвольные значения, что тоже очень здорово.
Здесь все вышеизложенное переделано для react-spring: https://codesandbox.io/s/clever-grothendieck-bxo6g?file=/src/App.js
Отказ от ответственности: у меня практически нулевой опыт работы с react-spring, поэтому вам обязательно следует прочитать документацию и посмотреть примеры.
Комментарии:
1. Спасибо, я заработал, но он действительно отстает, все еще работаю над обходными путями, есть идеи, что я мог бы сделать, чтобы сделать его плавным?
2. Я сделал это с помощью react-spring, и он воспроизводит анимацию примерно со скоростью 2 кадра в секунду…
3. Обнаружена эта проблема . По-видимому, вся страница принудительно перезаписывается, поэтому она мигает при каждом изменении масштаба.
4. Хм, да, это было то, с чем я столкнулся. Мне удалось решить проблему, используя renderTextLayer=false prop на странице, это позволяет отображать страницу в формате svg вместо серии пролетов, и это очень помогло с производительностью.