提交 28da44f2 编写于 作者: Q qiang

Merge branch 'feat' into dev

......@@ -5,13 +5,13 @@
"lint": "eslint --fix --config package.json --ext .js --ext .vue --ignore-path .eslintignore build src",
"dev:h5": "npm run lint && cross-env NODE_ENV=production UNI_WATCH=true UNI_PLATFORM=h5 node build/build.js",
"build:h5": "npm run lint && cross-env NODE_ENV=production UNI_WATCH=false UNI_PLATFORM=h5 node build/build.js",
"build:app-plus": "cross-env UNI_PLATFORM=app-plus rollup -c build/rollup.config.js",
"build:service:legacy": "npm run lint && rollup -c build/rollup.config.service.js",
"build:app-plus": "cross-env UNI_PLATFORM=app-plus rollup -c build/rollup.config.js",
"build:service:legacy": "npm run lint && rollup -c build/rollup.config.service.js",
"build:mp-qq": "cross-env UNI_PLATFORM=mp-qq rollup -c build/rollup.config.js",
"build:mp-weixin": "cross-env UNI_PLATFORM=mp-weixin rollup -c build/rollup.config.js",
"build:mp-baidu": "cross-env UNI_PLATFORM=mp-baidu rollup -c build/rollup.config.js",
"build:mp-alipay": "cross-env UNI_PLATFORM=mp-alipay rollup -c build/rollup.config.js",
"build:mp-toutiao": "cross-env UNI_PLATFORM=mp-toutiao rollup -c build/rollup.config.js",
"build:mp-toutiao": "cross-env UNI_PLATFORM=mp-toutiao rollup -c build/rollup.config.js",
"build:runtime": "npm run lint && npm run build:mp-weixin && npm run build:mp-qq && npm run build:mp-alipay && npm run build:mp-baidu && npm run build:mp-toutiao && npm run build:app-plus",
"test:unit": "cross-env NODE_ENV=test UNI_PLATFORM=h5 mocha-webpack --require tests/unit/setup.js --webpack-config build/webpack.config.test.js tests/unit/**/*.spec.js"
},
......@@ -38,6 +38,7 @@
"eslint-plugin-promise": "^4.0.0",
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^4.7.1",
"intersection-observer": "^0.7.0",
"jsdom": "^13.0.0",
"jsdom-global": "^3.0.2",
"rollup": "^0.67.4",
......
const defaultOption = {
duration: 400,
timingFunction: 'linear',
delay: 0,
transformOrigin: '50% 50% 0'
}
class MPAnimation {
constructor (option) {
this.actions = []
this.currentTransform = {}
this.currentStepAnimates = []
this.option = Object.assign({}, defaultOption, option)
}
_getOption (option) {
let _option = {
transition: Object.assign({}, this.option, option)
}
_option.transformOrigin = _option.transition.transformOrigin
delete _option.transition.transformOrigin
return _option
}
_pushAnimates (type, args) {
this.currentStepAnimates.push({
type: type,
args: args
})
}
_converType (type) {
return type.replace(/[A-Z]/g, text => {
return `-${text.toLowerCase()}`
})
}
_getValue (value) {
return typeof value === 'number' ? `${value}px` : value
}
export () {
const actions = this.actions
this.actions = []
return {
actions
}
}
step (option) {
this.currentStepAnimates.forEach(animate => {
if (animate.type !== 'style') {
this.currentTransform[animate.type] = animate
} else {
this.currentTransform[`${animate.type}.${animate.args[0]}`] = animate
}
})
this.actions.push({
animates: Object.values(this.currentTransform),
option: this._getOption(option)
})
this.currentStepAnimates = []
return this
}
}
const animateTypes1 = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d', 'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY', 'translateZ']
const animateTypes2 = ['opacity', 'backgroundColor']
const animateTypes3 = ['width', 'height', 'left', 'right', 'top', 'bottom']
animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => {
MPAnimation.prototype[type] = function (...args) {
if (animateTypes2.concat(animateTypes3).includes(type)) {
this._pushAnimates('style', [this._converType(type), animateTypes3.includes(type) ? this._getValue(args[0]) : args[0]])
} else {
this._pushAnimates(type, args)
}
return this
}
})
export function createAnimation (option) {
return new MPAnimation(option)
}
import Vue from 'vue'
import createCallbacks from 'uni-helpers/callbacks'
const createIntersectionObserverCallbacks = createCallbacks('requestComponentObserver')
const defaultOptions = {
thresholds: [0],
initialRatio: 0,
observeAll: false
}
class MPIntersectionObserver {
constructor (pageId, options) {
this.pageId = pageId
this.options = Object.assign({}, defaultOptions, options)
}
_makeRootMargin (margins = {}) {
this.options.rootMargin = ['top', 'right', 'bottom', 'left'].map(name => `${Number(margins[name]) || 0}px`).join(' ')
}
relativeTo (selector, margins) {
this.options.relativeToSelector = selector
this._makeRootMargin(margins)
return this
}
relativeToViewport (margins) {
this.options.relativeToSelector = null
this._makeRootMargin(margins)
return this
}
observe (selector, callback) {
if (typeof callback !== 'function') {
return
}
this.options.selector = selector
this.reqId = createIntersectionObserverCallbacks.push(callback)
UniServiceJSBridge.publishHandler('requestComponentObserver', {
reqId: this.reqId,
options: this.options
}, this.pageId)
}
disconnect () {
UniServiceJSBridge.publishHandler('destroyComponentObserver', {
reqId: this.reqId
}, this.pageId)
}
}
export function createIntersectionObserver (context, options) {
if (!(context instanceof Vue)) {
options = context
context = null
}
if (context) {
return new MPIntersectionObserver(context.$page.id, options)
}
const app = getApp()
if (app.$route && app.$route.params.__id__) {
return new MPIntersectionObserver(app.$route.params.__id__, options)
} else {
UniServiceJSBridge.emit('onError', 'createIntersectionObserver:fail')
}
}
......@@ -39,6 +39,7 @@ function onRequestComponentObserver ({
if (callback) {
if (reqEnd) {
requestComponentObserverCallbacks.pop(reqId)
return
}
callback(res)
}
......@@ -51,4 +52,4 @@ export default function initSubscribe (subscribe) {
subscribe('onRequestComponentInfo', onRequestComponentInfo)
subscribe('onRequestComponentObserver', onRequestComponentObserver)
}
}
......@@ -17,6 +17,8 @@ import {
import requestComponentInfo from './request-component-info'
import { requestComponentObserver, destroyComponentObserver } from './request-component-observer'
const passiveOptions = supportsPassive ? {
passive: false
} : false
......@@ -40,6 +42,9 @@ export default function initSubscribe (subscribe) {
subscribe('pageScrollTo', pageScrollTo)
subscribe('requestComponentObserver', requestComponentObserver)
subscribe('destroyComponentObserver', destroyComponentObserver)
if (__PLATFORM__ === 'h5') {
let scrollListener = false
let disableScrollListener = false
......@@ -48,7 +53,7 @@ export default function initSubscribe (subscribe) {
updateCssVar(vm)
})
subscribe('onPageShow', vm => {
subscribe('onPageShow', vm => {
const pageVm = vm.$parent.$parent
if (vm._isMounted) { // 非首次 show 才 update(首次 show 的时候在 onPageLoad 中触发了)
......@@ -87,4 +92,4 @@ export default function initSubscribe (subscribe) {
}
})
}
}
}
import 'intersection-observer'
import {
normalizeDataset
} from 'uni-helpers'
function getRect (rect) {
return {
bottom: rect.bottom,
height: rect.height,
left: rect.left,
right: rect.right,
top: rect.top,
width: rect.width
}
}
const intersectionObservers = {}
export function requestComponentObserver ({
reqId,
options
}, pageId) {
const pages = getCurrentPages()
const pageVm = pages.find(page => page.$page.id === pageId)
if (!pageVm) {
throw new Error(`Not Found:Page[${pageId}]`)
}
const $el = pageVm.$el
const root = options.relativeToSelector ? $el.querySelector(options.relativeToSelector) : null
let intersectionObserver = intersectionObservers[reqId] = new IntersectionObserver((entries, observer) => {
entries.forEach(entrie => {
UniViewJSBridge.publishHandler('onRequestComponentObserver', {
reqId,
res: {
intersectionRatio: entrie.intersectionRatio,
intersectionRect: getRect(entrie.intersectionRect),
boundingClientRect: getRect(entrie.boundingClientRect),
relativeRect: getRect(entrie.rootBounds),
time: Date.now(),
dataset: normalizeDataset(entrie.target.dataset || {}),
id: entrie.target.id
}
}, pageVm.$page.id)
})
}, {
root,
rootMargin: options.rootMargin,
threshold: options.thresholds
})
if (options.observeAll) {
intersectionObserver.USE_MUTATION_OBSERVER = true
Array.prototype.map.call($el.querySelectorAll(options.selector), el => {
intersectionObserver.observe(el)
})
} else {
intersectionObserver.USE_MUTATION_OBSERVER = false
intersectionObserver.observe($el.querySelector(options.selector))
}
}
export function destroyComponentObserver ({ reqId }) {
const intersectionObserver = intersectionObservers[reqId]
if (intersectionObserver) {
intersectionObserver.disconnect()
UniViewJSBridge.publishHandler('onRequestComponentObserver', {
reqId,
reqEnd: true
})
}
}
import Vue from 'vue'
import baseMixin from 'uni-mixins/base'
import animation from 'uni-mixins/animation'
const requireComponents = [
// baseComponents
......@@ -17,9 +18,11 @@ requireComponents.forEach((components, index) => {
componentConfig.mixins = componentConfig.mixins ? [].concat(baseMixin, componentConfig.mixins) : [baseMixin]
componentConfig.mixins.push(animation)
componentConfig.name = 'VUni' + componentConfig.name
// 全局注册组件
Vue.component(componentConfig.name, componentConfig)
})
})
})
function converPx (value) {
if (/\d+[ur]px$/i.test(value)) {
value.replace(/\d+[ur]px$/i, text => {
return `${uni.upx2px(parseFloat(text))}px`
})
// eslint-disable-next-line no-useless-escape
} else if (/^-?[\d\.]+$/.test(value)) {
return `${value}px`
}
return value || ''
}
function converType (type) {
return type.replace(/[A-Z]/g, text => {
return `-${text.toLowerCase()}`
}).replace('webkit', '-webkit')
}
function getStyle (action) {
const animateTypes1 = ['matrix', 'matrix3d', 'scale', 'scale3d', 'rotate3d', 'skew', 'translate', 'translate3d']
const animateTypes2 = ['scaleX', 'scaleY', 'scaleZ', 'rotate', 'rotateX', 'rotateY', 'rotateZ', 'skewX', 'skewY', 'translateX', 'translateY', 'translateZ']
const animateTypes3 = ['opacity', 'backgroundColor']
const animateTypes4 = ['width', 'height', 'left', 'right', 'top', 'bottom']
const animates = action.animates
const option = action.option
const transition = option.transition
const style = {}
let transform = []
animates.forEach(animate => {
const type = animate.type
let args = [...animate.args]
if (animateTypes1.concat(animateTypes2).includes(type)) {
if (type.startsWith('rotate') || type.startsWith('skew')) {
args = args.map(value => parseFloat(value) + 'deg')
} else if (type.startsWith('translate')) {
args = args.map(converPx)
}
if (animateTypes2.indexOf(type)) {
args.length = 1
}
transform.push(`${type}(${args.join(',')})`)
} else if (animateTypes3.concat(animateTypes4).includes(type)) {
const value = args[0]
style[type] = animateTypes4.includes(type) ? converPx(value) : value
}
})
style.transform = style.webkitTransform = transform.join(' ')
style.transition = style.webkitTransition = Object.keys(style).map(type => `${converType(type)} ${transition.duration}ms ${transition.timingFunction} ${transition.delay}ms`).join(',')
style.transformOrigin = style.webkitTransformOrigin = option.transformOrigin
return style
}
function startAnimation (context) {
const animation = context.animation
if (!animation || !animation.actions || !animation.actions.length) {
return
}
let index = 0
const actions = animation.actions
const length = animation.actions.length
function animate () {
const action = actions[index]
const transition = action.option.transition
const style = getStyle(action)
Object.keys(style).forEach(key => {
context.$el.style[key] = style[key]
})
index += 1
if (index < length) {
setTimeout(animate, transition.duration + transition.delay)
}
}
animate()
}
export default {
props: ['animation'],
watch: {
animation () {
startAnimation(this)
}
},
mounted () {
startAnimation(this)
}
}
......@@ -47,9 +47,7 @@ export default [
'stopBeaconDiscovery',
'setBackgroundColor',
'setBackgroundTextStyle',
'createAnimation',
'loadFontFace',
'createIntersectionObserver',
'getProvider',
'login',
'checkSession',
......
......@@ -3977,6 +3977,10 @@ interpret@^1.0.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296"
intersection-observer@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/intersection-observer/-/intersection-observer-0.7.0.tgz#ee16bee978db53516ead2f0a8154b09b400bbdc9"
invariant@^2.2.2:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册