Skip to content

Instantly share code, notes, and snippets.

@11111000000
Created March 28, 2017 19:58
Show Gist options
  • Save 11111000000/9a17013d1d988e8985942dd041d6f590 to your computer and use it in GitHub Desktop.
Save 11111000000/9a17013d1d988e8985942dd041d6f590 to your computer and use it in GitHub Desktop.

Origami

Компонент делает из плоского списка на входе, анимированную 3d гармошку. Декларативный JSX компонент, в котором также потребовалось динамически поддерживать обновление DOM, но делается это аккуратно.

Компонент

Чтобы обойтись минимумом скриптов и использовать лишь CSS3 для расщёта самой анимации, каждая следующая секция списка рекурсивно вкладывается в предыдущую (блок с reduceRight). Таким образом то, что за “сгибом”, уже поворачивается всё вместе не распадаясь на части.

import { element } from 'deku'
import toArray from 'to-array'
import c from 'classnames'

let log = debug('app:Origami')
let data = {}

const TRANSITION_FRAME_DELAY_MS = 1

export default {

  render({ children, props, path }) {

    const { scheme, folded } = props
    const origami = children.reduceRight( (acc, it, index) => {

      return (

        <div class={ c(['origami-fold', { 'odd' : ((index+1) % 2) != 0,
                                         'even' : ((index+1) % 2) == 0,
                                        'first' : index == 0 } ] ) } >

          { it }
          { acc }

         </div>)
    }, [])

    return (

      <div id={ path }
           class={ c([ 'origami',
                        (props.type || ''),
                        (props.class || ''), {
                          'folded' : folded,
                          'unfolded' : !folded
                        }]) }>
        { origami }
      </div>
    )
  },

При создании компонента, запускаем цикл обновления реальных границ элемента, с целью чтобы они постоянно соответствовали видимым. При удалении компонента - останавливаем этот цикл:

  onCreate({ path  }) {
    data[path] = {}

    requestAnimationFrame( () => {

      const element = document.getElementById( path ),
            stopFix = fixHeightCycle(element)

      data[path].stopFix = stopFix

    })
  },

  onUpdate({ props : { folded } , path }) {

    requestAnimationFrame( () => {} )

  },

  onRemove({ path }) {
    data[path] && data[path].stopFix()
  }
}

Функция расчёта реальной высоты:

function foldsHeight(element) {
  const folds = Array.from(element.querySelectorAll('.origami-fold')),

        height = folds.reduce((
          (acc, it) => {
            const firstChild = it.children[0]
            if(firstChild) {
              return acc + firstChild.getBoundingClientRect().height
            } else {
              return acc
            }
          } ), 0)

  return height
}

Цикл обновления реального размера.

requestAnimationFrame - способ делать это, дожидаясь отрисовки нового кадра.

function fixHeightCycle (element) {

  let requestId = null, state = { play : true }

  if(element) {
    requestAnimationFrame( frameHandler )
  }

  function frameHandler() {

    const firstFold = element.querySelector('.origami-fold'),

          rect = firstFold.getBoundingClientRect()

    element.style.maxHeight = foldsHeight(element) + 'px' //rect.height + 'px'

    if( state.play ) {

      setTimeout( () => {
        requestAnimationFrame( frameHandler )
      }, TRANSITION_FRAME_DELAY_MS)

    } else { return }

  }

  return function stop() { state.play = false }

}

Стиль

Благодаря вложенному списку исходных элементов, получился лаконичным:

.origami
  position relative
  perspective 1000px
  -moz-perspective 1000px
  perspective-origin 50% 0%
  overflow hidden
  max-height 2000px

  .origami-fold
    transform-style preserve-3d
    transform-origin top
    box-sizing border-box
    transition-duration .25s
    transition-delay 0s
    transition-timing-function ease-in-out

  &.folded
    max-height 0px
    .origami-fold
      transition-delay 0s
      &.first
        transform rotateX(-90deg) !important
      &.odd
        transform rotateX(-180deg)
      &.even
        transform rotateX(180deg)

  &.unfolded
    .origami-fold
      &.first
        transform rotateX(0) !important
      &.odd
        transform rotateX(0)
      &.even
        transform rotateX(0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment