Автор этой статьи: Ян Йе
Исходное заявление: Эта статья подготовлена членами команды интерфейса чтения YFE, пожалуйста, уважайте оригинальность, пожалуйста, свяжитесь с общедоступной учетной записью (id: yuewen_YFE) для авторизации и укажите автора, источник и ссылку.
задний план
в моем развитиироман-диалогВ ходе проекта мы обнаружили, что творческая деятельность очень помогла повысить данные о конверсиях. После исследования большинство групп пользователей этого разговорного нового продукта являются относительно молодыми после 90-х-95-х годов, поэтому окончательный вывод состоит в том, что мы надеемся использовать популярную в отрасли интерактивную форму молодого приложения - «Слайд-карту» для создания книги. рекламные мероприятия. После доработки я также надеюсь, что эту страницу можно будет объединить с самим продуктом в качестве страницы резидентной функции. Давайте посмотрим на окончательный эффект реализации:
Это гладко? Далее я расскажу, что происходило в разработке согласно мышлению и процессу разработки в то время.
Ссылаться на
После того, как очень внимательный дизайнер представил проект дизайна, она также специально использовалаflinto⤵️ Сделал интерактивный прототип, чтобы помочь мне добиться нужного эффекта планирования.
"интерактивный проект флинто"Увидев этот интимный интерактивный проект, первое, о чем я подумал, это обратиться к странице исследования в приложении Instant App и интерактивной форме программного обеспечения для знакомств «Tan Tan». Их интерактивные эффекты заключаются в следующем:
«Избиение приложения» «Мгновенное приложение — открытие»Эти два эффекта очень похожи? 😏 Но что отличается от моих потребностей на этот раз, так это то, что наша страница H5 встроена в приложение Qidian Reading, и оба вышеперечисленных эффекта являются результатом разработки собственного приложения, поэтому я не уверен в том, «может ли это быть сильно восстановлен» и «Как обеспечить хорошую производительность» все еще немного волнует 🤔.
пытаться
Идеи рефакторинга стиляПрежде чем получить реальные данные и разработать сложную логику, я сначала разобрал идеи реализации с эскизами:
как показано на рисунке:- Исходное состояние состоит в том, что 3 карты сложены вместе, и должно быть трехмерное трехмерное ощущение.При перетаскивании последние две карты могут быть открыты.
- При перетаскивании первой карты карта должна следовать направлению скольжения пальца.После того, как палец будет отпущен на определенное расстояние, карта вылетит, а следующие карты автоматически продвинутся на одну карту.Три карты всегда видны на страница.
В соответствии с приведенными выше идеями, поскольку существует потребность в 3D-стереоскопическом эффекте и эффекте движения, если для его достижения используется только z-индекс, он определенно не будет удовлетворен, поэтому я решил использовать translateZ, чтобы завершить эффект движения этого сложенная карта, потому что она может лучше отображать глубину резкости 3D-пространства. Таким образом, анимационные эффекты, такие как продвижение карты и автоматический вылет брошенной карты, могут быть полностью дополнены анимационными переходами CSS3.
Код стиля (основные структурные свойства)
.card_container {
position: relative;
width: 6.86rem;
height: 8.96rem;
perspective: 1000px;
perspective-origin: 50% 150%;
-webkit-perspective: 1000px;
-webkit-perspective-origin: 50% 150%;
}
.card {
transform-style: preserve-3d;
width: 100%;
height: 100%;
position: absolute;
opacity: 0;
}
Сложенные карты должны иметь родительский контейнер, который дает всем сложенным картам трехмерную перспективу.
HTML и методы привязки
<div class="card_container">
<div
v-for="(item,index) in dataArr"
:key="item.id"
ondragstart="return false"
class="card"
:style="[cardTransform(index),indexTransform(index)]"
@touchstart.stop.capture="touchStart($event,index)"
@touchmove.stop.capture="touchMove($event)"
@touchend.stop.capture="touchEnd($event,index)"
@mousedown.stop.capture="touchStart($event)"
@mousemove.stop.capture="touchMove($event)"
@mouseup.stop.capture="touchEnd"
@transitionend="onTransitionEnd(index)"
>
</div>
Нам также нужны некоторые ключевые переменные для записи некоторых свойств, которые могут изменяться в режиме реального времени:
// 当前展示的图片index
currentIndex: 0,
// 记录偏移量
displacement: {
x: 0,
y: 0
},
// 位置信息
position: {
start: { x: 0, y: 0 },
end: { x: 0, y: 0 },
direction: 1, // 滑动方向,左是-1,右是1
swipping: false // 是否在拖动交换过程中
},
// 记录每一个丢出去的方向
directionArr: [],
// 显示图片的堆叠数量
visible: 3,
// 视口宽度
winWidth: 0,
// 滑动阈值
slideWidth: 70,
// 超过阈值时的自动偏移量
offsetWidth: 120,
Затем привяжите к стилю два метода инициализации. cardTransform используется для инициализации стиля каждой карты, а indexTransform используется для инициализации стиля первой карты.
// 初始化每张卡片的样式
cardTransform (index) {
let style = {}
//卡片自动位移距离(飞出屏幕多远)
let offset = 0
if (this.directionArr[index] === 1) {
offset = 800
} else if (this.directionArr[index] === -1) {
offset = -800
}
style['z-index'] = this.currentIndex - index + this.visible
style['transform'] = `translate3d(0,0,${(this.currentIndex - index) * 60}px)`
//让藏在后面的卡片缩小样式堆叠在一起并透明不显示。一旦飞走一张,下一张卡片会自动过渡动画往前推进
if (index - this.currentIndex < 0) {
style['opacity'] = 0
style['transform'] = `translate3d(${this.position.end.x + offset}px,${this.position.end.y}px,${(this.currentIndex - index) * 60}px) rotate(${this.position.direction * -65}deg)`
}
// 非手势滑动状态才添加过渡动画
if (!this.position.swipping) {
style['transitionTimingFunction'] = 'ease'
style['transitionDuration'] = 300 + 'ms'
}
return style
},
// 第一张卡片的样式
indexTransform (index) {
let style = {}
if (index === this.currentIndex) {
style['transform'] = `translate3d(${this.displacement.x}px,${this.displacement.y}px,${(this.currentIndex - index) * 60}px) rotate(${this.displacement.x / this.winWidth * -65}deg)`
}
// 非手势滑动状态才添加过渡动画
if (!this.position.swipping) {
style['transitionTimingFunction'] = 'ease'
style['transitionDuration'] = 300 + 'ms'
}
return style
}
Последующее событие касания карты перетаскивания так же просто и легко, как запись и перетаскивание DIV до этого, а метод возврата к предыдущему и детали фонового перехода не будут показаны здесь слишком много кода.
До сих пор с использованием четырех фиктивных данных все шло гладко, а анимация была очень плавной:
Сбой веб-просмотра приложения 😱
Затем я начал запрашивать реальные данные и сделал ряд оптимизаций, таких как:
- Все модели адаптируются к центру экрана карты.
- Записывайте пользовательские операции и сохраняйте направление перетаскивания в localStorage (первая карта, которую пользователь видит, когда снова открывает ее, остается той, когда он ушел, и опыт больше похож на приложение)
- Оптимизируйте и уменьшите запросы, загружайте 2 изображения при первом входе на страницу и загружайте следующее изображение каждый раз, когда карта улетает.
После оптимизации все так плавно выглядит в мобильном режиме PC Chrome.Я думал, что проблем не будет.Наконец, когда я выпустил его в тестовую среду и открыл, просканировав код, то увидел такую сцену:
Мои опасения по поводу производительности в начале, наконец, оправдались. Приложение сразу рухнуло. Я попытался открыть его с помощью мобильного браузера, и оно не рухнуло, но работа не была гладкой. Я вернулся к ПК, чтобы испытать его снова. Я до сих пор не чувствую никаких заиканий. Я думаю, это может быть из-за того, что аппаратное обеспечение мобильного телефона не так хорошо, как у ПК. Причиной сбоя может быть проблема с 3D-рендерингом или производительностью. В соответствии с этой идеей я планирую сравнить данные, чтобы увидеть, какие ключевые элементы вызывают сбой.
Сравнение производительности
Сначала я использовал собственную производительность Chrome для записи страницы до 7 секунд, через 7 секунд я лихорадочно управлял картой, окончательный график производительности выглядит следующим образом:
За исключением небольшой оговорки: хендлер не доказывает ничего серьезного. Я собираюсь снова отслеживать производительность рендеринга, я открыл панель рендеринга из дополнительных инструментов Chrome.
После того, как все опции отмечены галочками, причина проблемы выявляется сразу!
ОМГ 😱, фреймрейт всего 18 fps, и получается, что все карты накладываются и рендерятся. Сразу понял неправильный момент в разработке:Хотя прозрачность этих скрытых карт установлена на 0, но невидимость не означает, что они не будут рендериться.Эти скрытые карты рендерятся в реальном времени после каждого вылета карты из анимации, что серьезно снижает производительность.
Другими словами, непрозрачность вызывала большое количество перекомпоновок страницы, и тут я вспомнил,И непрозрачность, и видимость вызовут перекомпоновку, а пока есть перекомпоновка, она обязательно вызовет перерисовку.,Толькоdisplay:none
Lightning можно избежать, потому что он полностью отделен от документооборота.С момента разработки этого требования я занимался оптимизацией восстановления страницы и динамических эффектов, но забыл об этом важном моменте.
оптимизация
Зная ключ к проблеме, справиться с ней гораздо проще, и непрозрачность все же необходимо сохранить, потому что переход динамического эффекта требует прозрачности для украшения, и отображение станет очень жестким. Так как используется VUE, то сделать это проще.Сначала добавляем ко всем массивам в данных атрибут display, по умолчанию false, а потом уже привязываем элемент card.:class="{display:item.display}"
, а затем установите для всех стилей карточек css значениеdisplay:none
<div class="card_container">
<div
v-for="(item,index) in dataArr"
:key="item.id"
ondragstart="return false"
class="card"
:style="[cardTransform(index),indexTransform(index)]"
@touchstart.stop.capture="touchStart($event,index)"
@touchmove.stop.capture="touchMove($event)"
@touchend.stop.capture="touchEnd($event,index)"
@mousedown.stop.capture="touchStart($event)"
@mousemove.stop.capture="touchMove($event)"
@mouseup.stop.capture="touchEnd"
@transitionend="onTransitionEnd(index)"
:class="{display:item.display}"
>
</div>
Сделайте его истинным, когда его нужно отобразить, и стиль изменится на блочный.
.card.display {
display: block;
opacity: 1;
}
Например, у меня есть метод moveNext, который перемещает карту при касании End.
touchEnd () {
this.position.swipping = false
this.position.end['x'] = this.displacement.x
this.position.end['y'] = this.displacement.y
// 判断滑动距离超过设定值时,自动飞出
if (this.displacement.x > this.slideWidth) {
this.moveNext(1) //往右
} else (this.displacement.x < -this.slideWidth) {
this.moveNext(-1) //往左
}
this.$nextTick(() => {
this.displacement.x = 0
this.displacement.y = 0
this.isDrag = false
})
}
Затем мы можем работать с индексом во время moveNext. В moveNext необходимо добавить отображение первой карты, отображаемой в данный момент, и тех, что лежат стопкой позади, а карты, которые исчезли, становятся скрытыми, чтобы цикл был бесшовным. Кроме того, поскольку данные неопределенны, чтобы избежать некоторых крайних случаев (таких какпервая картадальше илиПоследний счетКарт в будущем больше не будет, так что нужно делать детальную отказоустойчивость).
moveNext (direction) {
this.position.direction = direction
// 防止在最后倒数几张时操时出错
try {
this.dataArr[this.currentIndex + 3].display = true
} catch (e) {
}
// 防止在第一张时操作出错
if (this.currentIndex > 0) {
try {
this.dataArr[this.currentIndex - 1].display = false
} catch (e) {
}
}
this.currentIndex++ //每次让下一张卡片往前推进,反之 -- 就是返回上一张
!direction ? this.position.end['x'] -= this.offsetWidth : this.position.end['x'] += this.offsetWidth
this.position.end['y'] += this.offsetWidth / 2
}
После некоторой настройки и оптимизации я вызвал панель Rendering, чтобы увидеть результаты:
Как и ожидалось, частота кадров достигает нормальных 60 кадров в секунду.Независимо от того, как выполняется операция, всегда видны (рендерятся) только 3 карты, производительность значительно улучшена, и при возврате в приложение нет проблемы с вылетом.
Опыт сканирования кода (используйте приложение отправной точки, чтобы лучше видеть)
Суммировать
После этого сбоя, вызванного веб-просмотром приложения, я получил некоторые впечатления и выводы, и я надеюсь, что это будет полезно для вас, кто читает эту статью 😊.
- При моделировании нативной анимации приложений в Интернете, особенно на мобильных устройствах, необходимо быть очень осторожным при использовании расширенных свойств для динамического изменения элементов в режиме реального времени.
- «Визуальное восприятие» не является точным и не может использоваться в качестве основы для измерения, все должно сравниваться с данными о производительности в инструментах разработки.
- Перекомпоновка и перерисовка на стороне ПК вряд ли вызовут проблемы с производительностью, если у них нет менталитета, знающего, что в горах есть тигры, и предпочитают писать код.Однако возможности рендеринга мобильного терминала намного хуже. чем на стороне ПК.Когда CSS запускает перекомпоновку и перерисовку, это становится «убийцей производительности» на мобильной стороне. Поэтому, прежде чем выполнять требования и динамические эффекты, также необходимо заранее сделать психологическое ожидание производительности собственного плана.При рассмотрении производительности страницы анализ перекомпоновки и перерисовки также является точкой входа.