index.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. import baseComponent from '../helpers/baseComponent'
  2. import classNames from '../helpers/libs/classNames'
  3. import { useRect } from '../helpers/hooks/useDOM'
  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: ['prefixCls, position, theme, direction, reverse, buttonVisible, hideShadow, actionRotate, buttons, hoverClass', function(prefixCls, position, theme, direction, reverse, buttonVisible, hideShadow, actionRotate, buttons, hoverClass) {
  108. const wrap = classNames(prefixCls, {
  109. [`${prefixCls}--${position}`]: position,
  110. [`${prefixCls}--${theme}`]: theme,
  111. [`${prefixCls}--${direction}`]: direction,
  112. [`${prefixCls}--reverse`]: reverse,
  113. [`${prefixCls}--opened`]: buttonVisible,
  114. })
  115. const action = classNames(`${prefixCls}__action`, {
  116. [`${prefixCls}__action--hide-shadow`]: hideShadow,
  117. })
  118. const text = classNames(`${prefixCls}__text`, {
  119. [`${prefixCls}__text--rotate`]: buttonVisible && actionRotate,
  120. })
  121. const button = buttons.map((button) => {
  122. const wrap = classNames(`${prefixCls}__button`, {
  123. [`${prefixCls}__button--hide-shadow`]: button.hideShadow,
  124. [`${prefixCls}__button--disabled`]: button.disabled,
  125. [`${button.className}`]: button.className,
  126. })
  127. const hover = button.hoverClass && button.hoverClass !== 'default' ? button.hoverClass : `${prefixCls}__button--hover`
  128. return {
  129. wrap,
  130. hover,
  131. }
  132. })
  133. const icon = `${prefixCls}__icon`
  134. const label = `${prefixCls}__label`
  135. const backdrop = `${prefixCls}__backdrop`
  136. const hover = hoverClass && hoverClass !== 'default' ? hoverClass : `${prefixCls}--hover`
  137. return {
  138. wrap,
  139. action,
  140. text,
  141. button,
  142. icon,
  143. label,
  144. backdrop,
  145. hover,
  146. }
  147. }],
  148. },
  149. methods: {
  150. updated(buttonVisible) {
  151. if (this.data.buttonVisible !== buttonVisible) {
  152. this.setData({
  153. buttonVisible,
  154. })
  155. this.updateButtonStyle(!buttonVisible)
  156. }
  157. },
  158. onChange(buttonVisible) {
  159. if (!this.data.controlled) {
  160. this.updated(buttonVisible)
  161. }
  162. this.triggerEvent('change', { value: buttonVisible })
  163. },
  164. onToggle() {
  165. this.onChange(!this.data.buttonVisible)
  166. },
  167. onTap(e) {
  168. const { index, value } = e.currentTarget.dataset
  169. const params = {
  170. index,
  171. value,
  172. buttons: this.data.buttons,
  173. }
  174. if (!value.disabled) {
  175. this.triggerEvent('click', params)
  176. this.onChange(false)
  177. }
  178. },
  179. forceUpdateButtonStyle() {
  180. this.updateButtonStyle(!this.data.buttonVisible)
  181. },
  182. /**
  183. * 更新按钮组样式
  184. */
  185. updateButtonStyle(isReset) {
  186. const { prefixCls, buttons, duration, direction, spaceBetween, scale } = this.data
  187. const buttonStyle = []
  188. const sign = this.data.reverse ? 1 : -1
  189. const isH = direction === 'horizontal'
  190. // 重置样式
  191. if (isReset) {
  192. buttons.forEach(() => {
  193. buttonStyle.push('opacity: 0; transform: translate3d(0, 0, 0)')
  194. })
  195. if (this.data.buttonStyle !== buttonStyle) {
  196. this.setData({ buttonStyle })
  197. }
  198. return
  199. }
  200. // 更新样式
  201. useRect(`.${prefixCls}__action`, this).then((rect) => {
  202. switch (direction) {
  203. case 'horizontal':
  204. case 'vertical':
  205. buttons.forEach((_, index) => {
  206. const offset = `${sign * (rect.width + spaceBetween) * (index + 1)}`
  207. const style = setTransform(offset, scale, duration, isH)
  208. buttonStyle.push(style)
  209. })
  210. break
  211. case 'circle':
  212. const radius = rect.width + spaceBetween
  213. buttons.forEach((_, index) => {
  214. buttonStyle.push(this.getCircleStyle(index, radius))
  215. })
  216. break
  217. }
  218. if (this.data.buttonStyle !== buttonStyle) {
  219. this.setData({ buttonStyle })
  220. }
  221. })
  222. },
  223. /**
  224. * 获取圆形按钮的样式
  225. * @param {Number} index 当前按钮索引
  226. * @param {Number} radius 圆的半径
  227. */
  228. getCircleStyle(index, radius) {
  229. const { sAngle, eAngle, duration, scale } = this.data
  230. const { length } = this.data.buttons
  231. const { max, sin, cos, PI } = Math
  232. const startAngle = sAngle * PI / 180
  233. const endAngle = eAngle * PI / 180
  234. const points = endAngle % (2 * PI) === 0 ? length : max(1, length - 1)
  235. const currentAngle = startAngle + (endAngle - startAngle) / points * index
  236. let x = sin(currentAngle) * radius
  237. let y = cos(currentAngle) * radius
  238. x = parseFloat(x.toFixed(6))
  239. y = parseFloat(y.toFixed(6))
  240. const transform = `transform: scale(${scale}) translate3d(${x}px, ${y}px, 0)`
  241. return `opacity: 1; transition-duration: ${duration}ms; ${transform}`
  242. },
  243. bindgetuserinfo(e) {
  244. this.triggerEvent('getuserinfo', {...e.detail, ...e.currentTarget.dataset })
  245. },
  246. bindcontact(e) {
  247. this.triggerEvent('contact', {...e.detail, ...e.currentTarget.dataset })
  248. },
  249. bindgetphonenumber(e) {
  250. this.triggerEvent('getphonenumber', {...e.detail, ...e.currentTarget.dataset })
  251. },
  252. bindopensetting(e) {
  253. this.triggerEvent('opensetting', {...e.detail, ...e.currentTarget.dataset })
  254. },
  255. bindlaunchapp(e) {
  256. this.triggerEvent('launchapp', {...e.detail, ...e.currentTarget.dataset })
  257. },
  258. bindchooseavatar(e) {
  259. this.triggerEvent('chooseavatar', {...e.detail, ...e.currentTarget.dataset })
  260. },
  261. onError(e) {
  262. this.triggerEvent('error', {...e.detail, ...e.currentTarget.dataset })
  263. },
  264. },
  265. ready() {
  266. const { defaultVisible, visible, controlled } = this.data
  267. const buttonVisible = controlled ? visible : defaultVisible
  268. this.updated(buttonVisible)
  269. },
  270. })