Skip to content

Commit

Permalink
Merge pull request #1372 from didi/refactor-web-event
Browse files Browse the repository at this point in the history
Refactor web event
  • Loading branch information
hiyuki authored Feb 21, 2024
2 parents 823a279 + ca75e65 commit 9d2945e
Show file tree
Hide file tree
Showing 13 changed files with 119 additions and 121 deletions.
29 changes: 3 additions & 26 deletions packages/api-proxy/src/common/js/web.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { isBrowser } from './utils'

function webHandleSuccess (result, success, complete) {
typeof success === 'function' && success(result)
typeof complete === 'function' && complete(result)
Expand Down Expand Up @@ -35,30 +33,9 @@ function createDom (tag, attrs = {}, children = []) {
// 在H5中,直接绑定 click 可能出现延时问题,很多点击可以关闭的组件被唤出之后,有概率立马触发点击事件,导致组件被关闭。
// 使用该方法通过 touchstart 和 touchend 模拟 click 事件,解决延时问题。
function bindTap (dom, handler) {
const isTouchDevice = isBrowser && document && ('ontouchstart' in document.documentElement)
if (isTouchDevice) {
let startTime = 0; let x = 0; let y = 0
const touchStart = (e) => {
startTime = Date.now()
x = e.touches[0].pageX
y = e.touches[0].pageY
}
const touchEnd = (e) => {
if (Date.now() - startTime < 300 && Math.abs(e.changedTouches[0].pageX - x) < 10 && Math.abs(e.changedTouches[0].pageY - y) < 10) {
handler(e)
}
}
dom.addEventListener('touchstart', touchStart)
dom.addEventListener('touchend', touchEnd)
return () => {
dom.removeEventListener('touchstart', touchStart)
dom.removeEventListener('touchend', touchEnd)
}
} else {
dom.addEventListener('click', handler)
return () => {
dom.removeEventListener('click', handler)
}
dom.addEventListener('tap', handler)
return () => {
dom.removeEventListener('tap', handler)
}
}

Expand Down
6 changes: 5 additions & 1 deletion packages/api-proxy/src/platform/api/image/index.web.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Preview from './Preview'
import { envError } from '../../../common/js'
import { isBrowser, throwSSRWarning, envError } from '../../../common/js'

let preview = null

Expand All @@ -8,6 +8,10 @@ let preview = null
* @param {Object} options - 预览图片的配置项
*/
const previewImage = (options) => {
if (!isBrowser) {
throwSSRWarning('previewImage API is running in non browser environments')
return
}
if (!preview) preview = new Preview()
preview.show(options)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = function ({ print }) {
return {
test: TAG_NAME,
web (tag, { el }) {
if (el.hasEvent) {
if (el.hasModel) {
el.isBuiltIn = true
}
if (el.isBuiltIn) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ module.exports = function ({ print }) {
return {
test: TAG_NAME,
web (tag, { el }) {
if (el.hasEvent) {
if (el.hasModel) {
el.isBuiltIn = true
}

if (el.isBuiltIn) {
return 'mpx-text'
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module.exports = function ({ print }) {
// return 'a:view'
// },
web (tag, { el }) {
if (el.hasEvent) {
if (el.hasModel) {
el.isBuiltIn = true
}
if (el.isBuiltIn) {
Expand Down
4 changes: 1 addition & 3 deletions packages/webpack-plugin/lib/platform/template/wx/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ module.exports = function getSpec ({ warn, error }) {
{
test: 'wx:model',
web ({ value }, { el }) {
el.hasEvent = true
el.hasModel = true
const attrsMap = el.attrsMap
const tagRE = /\{\{((?:.|\n|\r)+?)\}\}(?!})/
const stringify = JSON.stringify
Expand Down Expand Up @@ -387,8 +387,6 @@ module.exports = function getSpec ({ warn, error }) {
modifierStr
}
const isComponent = usingComponents.indexOf(el.tag) !== -1 || el.tag === 'component'
// 记录event监听信息用于后续判断是否需要使用内置基础组件
el.hasEvent = true
const rPrefix = runRules(spec.event.prefix, prefix, { mode: 'web', meta })
const rEventName = runRules(eventRules, eventName, { mode: 'web', data: { isComponent } })
return {
Expand Down
100 changes: 100 additions & 0 deletions packages/webpack-plugin/lib/runtime/components/web/event.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { extendEvent } from './getInnerListeners'
import { isBrowser } from '../../env'

function MpxEvent (layer) {
this.targetElement = null

this.touches = []

this.touchStartX = 0

this.touchStartY = 0

this.startTimer = null

this.needTap = true

this.isTouchDevice = document && ('ontouchstart' in document.documentElement)

this.onTouchStart = (event) => {
if (event.targetTouches?.length > 1) {
return true
}

this.touches = event.targetTouches
this.targetElement = event.target
this.needTap = true
this.startTimer = null
this.touchStartX = this.touches[0].pageX
this.touchStartY = this.touches[0].pageY
this.startTimer = setTimeout(() => {
this.needTap = false
this.sendEvent(this.targetElement, 'longpress', event)
this.sendEvent(this.targetElement, 'longtap', event)
}, 350)
}

this.onTouchMove = (event) => {
const touch = event.changedTouches[0]
if (Math.abs(touch.pageX - this.touchStartX) > 1 || Math.abs(touch.pageY - this.touchStartY) > 1) {
this.needTap = false
this.startTimer && clearTimeout(this.startTimer)
this.startTimer = null
}
}

this.onTouchEnd = (event) => {
if (event.targetTouches?.length > 1) {
return true
}
this.startTimer && clearTimeout(this.startTimer)
this.startTimer = null
if (this.needTap) {
this.sendEvent(this.targetElement, 'tap', event)
}
}

this.onClick = (event) => {
this.targetElement = event.target
this.sendEvent(this.targetElement, 'tap', event)
}
this.sendEvent = (targetElement, type, event) => {
// eslint-disable-next-line no-undef
const touchEvent = new TouchEvent(type, {
view: window,
bubbles: true,
cancelable: true
})
const changedTouches = event.changedTouches || []
extendEvent(touchEvent, {
timeStamp: event.timeStamp,
currentTarget: event.target,
changedTouches,
touches: changedTouches,
detail: {
// pc端点击事件可能没有changedTouches,所以直接从 event中取
x: changedTouches[0]?.pageX || event.pageX || 0,
y: changedTouches[0]?.pageY || event.pageY || 0
}
})
targetElement && targetElement.dispatchEvent(touchEvent)
}

this.addListener = () => {
if (this.isTouchDevice) {
layer.addEventListener('touchstart', this.onTouchStart, true)
layer.addEventListener('touchmove', this.onTouchMove, true)
layer.addEventListener('touchend', this.onTouchEnd, true)
} else {
layer.addEventListener('click', this.onClick, true)
}
}
this.addListener()
}

if (isBrowser) {
document.addEventListener('DOMContentLoaded', function () {
// eslint-disable-next-line no-new
new MpxEvent(document.body)
}, false)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import { isEmptyObject } from '../../utils'
import { isBrowser } from '../../env'

function processModel (listeners, context) {
// 该函数只有wx:model的情况下才调用,而且默认e.detail.value有值
// 该函数必须在产生merge前执行
Expand Down Expand Up @@ -44,80 +41,6 @@ function mergeListeners (listeners, otherListeners, options = {}) {
})
}

function processTap (listeners, context) {
const listenerMap = {}
const tapEvents = ['tap', 'longpress', 'longtap']
const isTouchDevice = isBrowser ? document && ('ontouchstart' in document.documentElement) : true
tapEvents.forEach((eventName) => {
if (listeners[eventName]) {
listenerMap[eventName] = true
delete listeners[eventName]
}
})
if (isEmptyObject(listenerMap)) return
context.__mpxTapInfo = context.__mpxTapInfo || {}
let events
if (isTouchDevice) {
events = {
touchstart (e) {
context.__mpxTapInfo.detail = {
x: e.changedTouches[0].pageX,
y: e.changedTouches[0].pageY
}
context.__mpxTapInfo.startTimer = null
context.__mpxTapInfo.needTap = true
if (listenerMap.longpress || listenerMap.longtap) {
context.__mpxTapInfo.startTimer = setTimeout(() => {
context.__mpxTapInfo.needTap = false
if (listenerMap.longpress) {
const re = inheritEvent('longpress', e, context.__mpxTapInfo.detail)
context.$emit('longpress', re)
}
if (listenerMap.longtap) {
const re = inheritEvent('longtap', e, context.__mpxTapInfo.detail)
context.$emit('longtap', re)
}
}, 350)
}
},
touchmove (e) {
const tapDetailInfo = context.__mpxTapInfo.detail || {}
const currentPageX = e.changedTouches[0].pageX
const currentPageY = e.changedTouches[0].pageY
if (Math.abs(currentPageX - tapDetailInfo.x) > 1 || Math.abs(currentPageY - tapDetailInfo.y) > 1) {
context.__mpxTapInfo.needTap = false
context.__mpxTapInfo.startTimer && clearTimeout(context.__mpxTapInfo.startTimer)
context.__mpxTapInfo.startTimer = null
}
},
touchend (e) {
context.__mpxTapInfo.startTimer && clearTimeout(context.__mpxTapInfo.startTimer)
if (listenerMap.tap && context.__mpxTapInfo.needTap) {
const re = inheritEvent('tap', e, context.__mpxTapInfo.detail)
context.$emit('tap', re)
}
}
}
} else {
events = {
click (e) {
if (listenerMap.tap) {
context.__mpxTapInfo.detail = {
x: e.pageX,
y: e.pageY
}
const re = inheritEvent('tap', e, context.__mpxTapInfo.detail)
context.$emit('tap', re)
}
}
}
}

mergeListeners(listeners, events, {
force: true
})
}

export function extendEvent (e, extendObj = {}) {
Object.keys(extendObj).forEach((key) => {
Object.defineProperty(e, key, {
Expand Down Expand Up @@ -181,7 +104,6 @@ export default function getInnerListeners (context, options = {}) {
}

processModel(listeners, context)
processTap(listeners, context)
mergeListeners(listeners, mergeBefore, mergeBeforeOptions)
mergeListeners(listeners, mergeAfter, mergeAfterOptions)
ignoredListeners.forEach((key) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
},
mounted () {
if (this.formType) {
this.$on('tap', () => {
this.$el.addEventListener('tap', () => {
if (this.form && this.form[this.formType]) {
this.form[this.formType]()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
}
},
mounted () {
this.$on('tap', () => {
this.$el.addEventListener('tap', () => {
const mpx = global.__mpx
if (mpx) {
switch (this.openType) {
Expand Down
1 change: 0 additions & 1 deletion packages/webpack-plugin/lib/template-compiler/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -1860,7 +1860,6 @@ function getVirtualHostRoot (options, meta) {
value: '$listeners'
}
])
rootView.hasEvent = true
processElement(rootView, rootView, options, meta)
return rootView
}
Expand Down
3 changes: 3 additions & 0 deletions packages/webpack-plugin/lib/web/processMainScript.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
const addQuery = require('../utils/add-query')
const normalize = require('../utils/normalize')
const optionProcessorPath = normalize.lib('runtime/optionProcessor')
const eventPath = normalize.lib('runtime/components/web/event')
const {
buildComponentsMap,
buildPagesMap,
Expand Down Expand Up @@ -65,6 +66,8 @@ module.exports = function (script, {
globalTabBar
})

output += `\n require(${stringifyRequest(loaderContext, eventPath)})\n`

output += `\n var App = require(${stringifyRequest(loaderContext, addQuery(resource, { isApp: true }))}).default\n`

output += `
Expand Down
8 changes: 1 addition & 7 deletions test/utils/touch.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,5 @@ export const createTouchEvent = (type, target, params = {}) => {
}

export const dispatchTap = (el) => {
const isTouchDevice = typeof window !== 'undefined' && document && ('ontouchstart' in document.documentElement)
if (isTouchDevice) {
el.dispatchEvent(createTouchEvent('touchstart', el))
el.dispatchEvent(createTouchEvent('touchend', el))
} else {
el.dispatchEvent(new MouseEvent('click', { bubbles: true }))
}
el.dispatchEvent(createTouchEvent('tap', el))
}

0 comments on commit 9d2945e

Please sign in to comment.