blob: 0e5d6590758647dde05127d690c6cdb91c47afc6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
'use strict'
const proto = {
create () {
const node = document.createElement('div')
node.classList.add('weex-container')
node.style.overflow = 'hidden'
// fix page shaking during slider's playing
node.style.webkitTransform = 'translate3D(0,0,0)'
node.addEventListener('webkitTransitionEnd', this.end.bind(this), false)
return node
},
createChildren () {
// first run:
// - create each child
// - append to parentNode
// - find current and next
// - set current and next shown and others hidden
const children = this.data.children
const parentRef = this.data.ref
const instanceId = this.data.instanceId
const items = []
const componentManager = this.getComponentManager()
let fragment, isFlex, child, i
if (children && children.length) {
fragment = document.createDocumentFragment()
isFlex = false
for (i = 0; i < children.length; i++) {
children[i].instanceId = instanceId
child = componentManager.createElement(children[i])
child.parentRef = parentRef
this.initChild(child)
// append and push
items.push(child)
fragment.appendChild(child.node)
if (!isFlex && child.data.style.hasOwnProperty('flex')) {
isFlex = true
}
}
this.node.appendChild(fragment)
}
// set items
this.items = items
// reset the clock for first transition
this.reset()
},
appendChild (data) {
// dom + items
const componentManager = this.getComponentManager()
// const componentManager = ComponentManager.getInstance(this.data.instanceId)
const child = componentManager.createElement(data)
this.initChild(child)
this.node.appendChild(child.node)
this.items.push(child)
this.reset()
return child // @todo redesign Component#appendChild(component)
},
insertBefore (child, before) {
// dom + items
const index = this.items.indexOf(before)
this.items.splice(index, 0, child)
this.initChild(child)
this.node.insertBefore(child.node, before.node)
this.reset()
},
removeChild (child) {
// dom + items
const index = this.items.indexOf(child)
this.items.splice(index, 1)
this.node.removeChild(child.node)
this.reset()
},
initChild (child) {
const node = child.node
node.style.position = 'absolute'
node.style.top = '0'
node.style.left = '0'
},
/**
* status: {
* current: {translateY: 0, shown: true},
* next: {translateY: height, shown: true},
* others[]: {shown: false}
* index: index
* }
*/
reset () {
const interval = this.interval - 0
const delay = this.delay - 0
const items = this.items
const self = this
const loop = function () {
self.next()
self.timerId = setTimeout(loop, self.interval)
}
// reset display and transform
items.forEach(function (item, index) {
const node = item.node
// set non-current(0)|next(1) item hidden
node.style.display = index > 1 ? 'none' : ''
// set next(1) item translateY
// TODO: it supposed to use item.data.style
// but somehow the style object is empty.
// This problem relies on jsframework's bugfix.
// node.style.transform = index === 1
// ? 'translate3D(0,' + item.data.style.height + 'px,0)'
// : ''
// node.style.webkitTransform = index === 1
// ? 'translate3D(0,' + item.data.style.height + 'px,0)'
// : ''
node.style.transform = index === 1
? 'translate3D(0,' + self.data.style.height + 'px,0)'
: ''
node.style.webkitTransform = index === 1
? 'translate3D(0,' + self.data.style.height + 'px,0)'
: ''
})
setTimeout(function () {
// reset current, next, index
self.currentItem = items[0]
self.nextItem = items[1]
self.currentIndex = 0
items.forEach(function (item, index) {
const node = item.node
// set transition
node.style.transition = 'transform '
+ self.transitionDuration
+ 'ms ease'
node.style.webkitTransition = '-webkit-transform '
+ self.transitionDuration
+ 'ms ease'
})
clearTimeout(self.timerId)
if (items.length > 1) {
self.timerId = setTimeout(loop, delay + interval)
}
}, 13)
},
/**
* next:
* - current: {translateY: -height}
* - next: {translateY: 0}
*/
next () {
// - update state
// - set current and next transition
// - hide current when transition end
// - set next to current
// - find new next
const next = this.nextItem.node
const current = this.currentItem.node
this.transitionIndex = this.currentIndex
// Use setTimeout to fix the problem that when the
// page recover from backstage, the slider will
// not work any longer.
setTimeout(function () {
next.style.transform = 'translate3D(0,0,0)'
next.style.webkitTransform = 'translate3D(0,0,0)'
current.style.transform = 'translate3D(0,-'
+ this.data.style.height
+ 'px,0)'
current.style.webkitTransform = 'translate3D(0,-'
+ this.data.style.height
+ 'px,0)'
this.fireEvent('change')
}.bind(this), 300)
},
fireEvent (type) {
const length = this.items.length
const nextIndex = (this.currentIndex + 1) % length
const evt = document.createEvent('HTMLEvents')
evt.initEvent(type, false, false)
evt.data = {
prevIndex: this.currentIndex,
index: nextIndex
}
this.node.dispatchEvent(evt)
},
/**
* end:
* - old current: {shown: false}
* - old current: {translateY: 0}
* - index++ % length
* - new current = old next
* - new next = items[index+1 % length]
* - new next: {translateY: height}
* - new next: {shown: true}
*/
end (e) {
const items = this.items
const length = items.length
let currentIndex
currentIndex = this.transitionIndex
if (isNaN(currentIndex)) {
return
}
delete this.transitionIndex
const current = this.currentItem.node
current.style.display = 'none'
current.style.webkitTransform = ''
currentIndex = (currentIndex + 1) % length
const nextIndex = (currentIndex + 1) % length
this.currentIndex = currentIndex
this.currentItem = this.nextItem
this.nextItem = items[nextIndex]
setTimeout(function () {
const next = this.nextItem.node
// TODO: it supposed to use this.nextItem.data.style
// but somehow the style object is empty.
// This problem relies on jsframework's bugfix.
next.style.webkitTransform = 'translate3D(0,'
+ this.data.style.height
+ 'px,0)'
next.style.display = ''
this.fireLazyload(next)
}.bind(this))
},
clearAttr () {
this.interval = 5 * 1000
this.transitionDuration = 500
this.delay = 0
}
}
const attr = {
interval: function (value) {
this.interval = value
},
transitionDuration: function (value) {
this.transitionDuration = value
},
delay: function (value) {
this.delay = value
}
}
function init (Weex) {
const Component = Weex.Component
const extend = Weex.utils.extend
function Marquee (data) {
this.interval = Number(data.attr.interval) || 5 * 1000
this.transitionDuration = Number(data.attr.transitionDuration) || 500
this.delay = Number(data.attr.delay) || 0
Component.call(this, data)
}
Marquee.prototype = Object.create(Component.prototype)
extend(Marquee.prototype, proto)
extend(Marquee.prototype, { attr })
Weex.registerComponent('marquee', Marquee)
}
export default { init }