index.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. import baseComponent from '../helpers/baseComponent'
  2. import classNames from '../helpers/classNames'
  3. import { getTouchPoints, getPointsNumber } from '../helpers/gestures'
  4. const defaultAction = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAQAAAAAYLlVAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAAHdElNRQfhBAQLCR5MtjrbAAAAjUlEQVRo3u3ZMRKAIAxEUbDirp4nXnctFFDHBtDQ/O1Nnk6aHUMgZCBKMkmmNAtgOmL9M+IQQGVM95zljy8DAAAAAAAAAAAAAACALsDZcppSx7Q+WdtUvA5xffUtrjeA8/qQ21S9gc15/3Nfzw0M5O0G2kM5BQAAAAAAAAAAAAAAQGk33q0qZ/p/Q/JFdmei9usomnwIAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTA0LTA0VDExOjA5OjMwKzA4OjAw1U4c3wAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0wNC0wNFQxMTowOTozMCswODowMKQTpGMAAAAASUVORK5CYII='
  5. // 设置元素旋转属性
  6. const setTransform = (translate = 0, scale = 1, delay = 300, isH = true) => {
  7. const duration = `transition-duration: ${delay}ms`
  8. const transform = `transform: scale(${scale}) translate3d(${isH ? translate : 0}px, ${isH ? 0 : translate}px, 0)`
  9. return `opacity: 1; ${duration}; ${transform}`
  10. }
  11. baseComponent({
  12. properties: {
  13. prefixCls: {
  14. type: String,
  15. value: 'wux-fab-button',
  16. },
  17. hoverClass: {
  18. type: String,
  19. value: 'default',
  20. },
  21. theme: {
  22. type: String,
  23. value: 'balanced',
  24. },
  25. position: {
  26. type: String,
  27. value: 'bottomRight',
  28. },
  29. action: {
  30. type: String,
  31. value: defaultAction,
  32. },
  33. actionRotate: {
  34. type: Boolean,
  35. value: true,
  36. },
  37. hideShadow: {
  38. type: Boolean,
  39. value: false,
  40. },
  41. backdrop: {
  42. type: Boolean,
  43. value: false,
  44. },
  45. buttons: {
  46. type: Array,
  47. value: [],
  48. observer: 'forceUpdateButtonStyle',
  49. },
  50. direction: {
  51. type: String,
  52. value: 'horizontal',
  53. observer: 'forceUpdateButtonStyle',
  54. },
  55. spaceBetween: {
  56. type: Number,
  57. value: 10,
  58. observer: 'forceUpdateButtonStyle',
  59. },
  60. duration: {
  61. type: Number,
  62. value: 300,
  63. },
  64. scale: {
  65. type: Number,
  66. value: .9,
  67. observer: 'forceUpdateButtonStyle',
  68. },
  69. reverse: {
  70. type: Boolean,
  71. value: false,
  72. observer: 'forceUpdateButtonStyle',
  73. },
  74. sAngle: {
  75. type: Number,
  76. value: 0,
  77. observer: 'forceUpdateButtonStyle',
  78. },
  79. eAngle: {
  80. type: Number,
  81. value: 360,
  82. observer: 'forceUpdateButtonStyle',
  83. },
  84. defaultVisible: {
  85. type: Boolean,
  86. value: false,
  87. },
  88. visible: {
  89. type: Boolean,
  90. value: false,
  91. observer(newVal) {
  92. if (this.data.controlled) {
  93. this.updated(newVal)
  94. }
  95. },
  96. },
  97. controlled: {
  98. type: Boolean,
  99. value: false,
  100. },
  101. },
  102. data: {
  103. buttonStyle: [],
  104. buttonVisible: false,
  105. },
  106. computed: {
  107. classes() {
  108. const {
  109. prefixCls,
  110. position,
  111. theme,
  112. direction,
  113. reverse,
  114. buttonVisible,
  115. hideShadow,
  116. actionRotate,
  117. buttons,
  118. hoverClass,
  119. } = this.data
  120. const wrap = classNames(prefixCls, {
  121. [`${prefixCls}--${position}`]: position,
  122. [`${prefixCls}--${theme}`]: theme,
  123. [`${prefixCls}--${direction}`]: direction,
  124. [`${prefixCls}--reverse`]: reverse,
  125. [`${prefixCls}--opened`]: buttonVisible,
  126. })
  127. const action = classNames(`${prefixCls}__action`, {
  128. [`${prefixCls}__action--hide-shadow`]: hideShadow,
  129. })
  130. const text = classNames(`${prefixCls}__text`, {
  131. [`${prefixCls}__text--rotate`]: buttonVisible && actionRotate,
  132. })
  133. const button = buttons.map((button) => {
  134. const wrap = classNames(`${prefixCls}__button`, {
  135. [`${prefixCls}__button--hide-shadow`]: button.hideShadow,
  136. [`${prefixCls}__button--disabled`]: button.disabled,
  137. [`${button.className}`]: button.className,
  138. })
  139. const hover = button.hoverClass && button.hoverClass !== 'default' ? button.hoverClass : `${prefixCls}__button--hover`
  140. return {
  141. wrap,
  142. hover,
  143. }
  144. })
  145. const icon = `${prefixCls}__icon`
  146. const label = `${prefixCls}__label`
  147. const backdrop = `${prefixCls}__backdrop`
  148. const hover = hoverClass && hoverClass !== 'default' ? hoverClass : `${prefixCls}--hover`
  149. return {
  150. wrap,
  151. action,
  152. text,
  153. button,
  154. icon,
  155. label,
  156. backdrop,
  157. hover,
  158. }
  159. },
  160. },
  161. methods: {
  162. updated(buttonVisible) {
  163. if (this.data.buttonVisible !== buttonVisible) {
  164. this.setData({
  165. buttonVisible,
  166. })
  167. this.updateButtonStyle(!buttonVisible)
  168. }
  169. },
  170. onChange(buttonVisible) {
  171. if (!this.data.controlled) {
  172. this.updated(buttonVisible)
  173. }
  174. this.triggerEvent('change', { value: buttonVisible })
  175. },
  176. onToggle() {
  177. this.onChange(!this.data.buttonVisible)
  178. },
  179. onTap(e) {
  180. const { index, value } = e.currentTarget.dataset
  181. const params = {
  182. index,
  183. value,
  184. buttons: this.data.buttons,
  185. }
  186. if (!value.disabled) {
  187. this.triggerEvent('click', params)
  188. this.onChange(false)
  189. }
  190. },
  191. /**
  192. * 获取界面上的节点信息
  193. */
  194. getRect(selector, all) {
  195. return new Promise((resolve) => {
  196. wx
  197. .createSelectorQuery()
  198. .in(this)[all ? 'selectAll' : 'select'](selector)
  199. .boundingClientRect((rect) => {
  200. if (all && Array.isArray(rect) && rect.length) {
  201. resolve(rect)
  202. }
  203. if (!all && rect) {
  204. resolve(rect)
  205. }
  206. })
  207. .exec()
  208. })
  209. },
  210. forceUpdateButtonStyle() {
  211. this.updateButtonStyle(!this.data.buttonVisible)
  212. },
  213. /**
  214. * 更新按钮组样式
  215. */
  216. updateButtonStyle(isReset) {
  217. const { prefixCls, buttons, duration, direction, spaceBetween, scale } = this.data
  218. const buttonStyle = []
  219. const sign = this.data.reverse ? 1 : -1
  220. const isH = direction === 'horizontal'
  221. // 重置样式
  222. if (isReset) {
  223. buttons.forEach(() => {
  224. buttonStyle.push('opacity: 0; transform: translate3d(0, 0, 0)')
  225. })
  226. if (this.data.buttonStyle !== buttonStyle) {
  227. this.setData({ buttonStyle })
  228. }
  229. return
  230. }
  231. // 更新样式
  232. this.getRect(`.${prefixCls}__action`).then((rect) => {
  233. switch (direction) {
  234. case 'horizontal':
  235. case 'vertical':
  236. buttons.forEach((_, index) => {
  237. const offset = `${sign * (rect.width + spaceBetween) * (index + 1)}`
  238. const style = setTransform(offset, scale, duration, isH)
  239. buttonStyle.push(style)
  240. })
  241. break
  242. case 'circle':
  243. const radius = rect.width + spaceBetween
  244. buttons.forEach((_, index) => {
  245. buttonStyle.push(this.getCircleStyle(index, radius))
  246. })
  247. break
  248. }
  249. if (this.data.buttonStyle !== buttonStyle) {
  250. this.setData({ buttonStyle })
  251. }
  252. })
  253. },
  254. /**
  255. * 获取圆形按钮的样式
  256. * @param {Number} index 当前按钮索引
  257. * @param {Number} radius 圆的半径
  258. */
  259. getCircleStyle(index, radius) {
  260. const { sAngle, eAngle, duration, scale } = this.data
  261. const { length } = this.data.buttons
  262. const { max, sin, cos, PI } = Math
  263. const startAngle = sAngle * PI / 180
  264. const endAngle = eAngle * PI / 180
  265. const points = endAngle % (2 * PI) === 0 ? length : max(1, length - 1)
  266. const currentAngle = startAngle + (endAngle - startAngle) / points * index
  267. let x = sin(currentAngle) * radius
  268. let y = cos(currentAngle) * radius
  269. x = parseFloat(x.toFixed(6))
  270. y = parseFloat(y.toFixed(6))
  271. const transform = `transform: scale(${scale}) translate3d(${x}px, ${y}px, 0)`
  272. return `opacity: 1; transition-duration: ${duration}ms; ${transform}`
  273. },
  274. bindgetuserinfo(e) {
  275. this.triggerEvent('getuserinfo', {...e.detail, ...e.currentTarget.dataset })
  276. },
  277. bindcontact(e) {
  278. this.triggerEvent('contact', {...e.detail, ...e.currentTarget.dataset })
  279. },
  280. bindgetphonenumber(e) {
  281. this.triggerEvent('getphonenumber', {...e.detail, ...e.currentTarget.dataset })
  282. },
  283. bindopensetting(e) {
  284. this.triggerEvent('opensetting', {...e.detail, ...e.currentTarget.dataset })
  285. },
  286. onError(e) {
  287. this.triggerEvent('error', {...e.detail, ...e.currentTarget.dataset })
  288. },
  289. },
  290. ready() {
  291. const { defaultVisible, visible, controlled } = this.data
  292. const buttonVisible = controlled ? visible : defaultVisible
  293. this.updated(buttonVisible)
  294. },
  295. })