easy-hover.vue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. <template>
  2. <view class="hover-wrapper"
  3. :style="{ 'width': width + 'rpx', 'height': height + 'rpx', 'border-radius': circle ? '50%' : '0', 'top': top + 'px', 'left': left + 'px' }"
  4. @touchmove.prevent="touchmove" @touchend="touchend" @tap="doTap">
  5. <image :src="iconUrl" mode="aspectFill" class="icon"
  6. :style="{ 'width': width + 'rpx', 'height': height + 'rpx', 'border-radius': circle ? '50%' : '0' }"></image>
  7. </view>
  8. </template>
  9. <script>
  10. export default {
  11. name: 'easy-hover',
  12. props: {
  13. /**
  14. * 初始化方向
  15. */
  16. initSide: {
  17. type: String,
  18. default: 'right'
  19. },
  20. /**
  21. * 初始化距离上部距离rpx
  22. */
  23. initMarginTop: {
  24. type: Number,
  25. default: 100
  26. },
  27. /**
  28. * 图标地址
  29. */
  30. iconUrl: {
  31. type: String,
  32. default: ''
  33. },
  34. /**
  35. * 宽度
  36. */
  37. width: {
  38. type: Number,
  39. default: 200
  40. },
  41. /**
  42. * 高度
  43. */
  44. height: {
  45. type: Number,
  46. default: 200
  47. },
  48. /**
  49. * 是否是圆形
  50. */
  51. circle: {
  52. type: Boolean,
  53. default: false
  54. },
  55. /**
  56. * 是否贴边
  57. */
  58. stickSide: {
  59. type: Boolean,
  60. default: true
  61. }
  62. },
  63. data() {
  64. return {
  65. screenWidthMax: 0,
  66. screenHeightMax: 0,
  67. widthMiddle: 0,
  68. xOffset: 0,
  69. yOffset: 0,
  70. menuHeight: 0,
  71. isMove: false,
  72. top: 0,
  73. left: 0,
  74. animation: {},
  75. animationData: {}
  76. }
  77. },
  78. methods: {
  79. /**
  80. * 初始化参数封装,主要处理界面参数
  81. */
  82. initParams() {
  83. try {
  84. // 获取窗口信息
  85. let systemInfo = {}
  86. try {
  87. systemInfo = uni.getSystemInfoSync()
  88. } catch (e) {
  89. // 获取系统信息失败时使用默认值
  90. systemInfo = {
  91. windowWidth: 375,
  92. windowHeight: 667,
  93. statusBarHeight: 20
  94. }
  95. }
  96. this.screenWidthMax = systemInfo.windowWidth
  97. this.screenHeightMax = systemInfo.windowHeight
  98. this.widthMiddle = systemInfo.windowWidth / 2
  99. // #ifdef H5
  100. this.menuHeight = 0
  101. // #endif
  102. // #ifdef MP-WEIXIN
  103. // 获取微信小程序胶囊按钮位置信息
  104. const menuButtonInfo = wx.getMenuButtonBoundingClientRect()
  105. this.menuHeight = menuButtonInfo.top || 0
  106. // #endif
  107. // #ifndef H5 || MP-WEIXIN
  108. this.menuHeight = systemInfo.statusBarHeight || 0
  109. // #endif
  110. //计算偏移量
  111. this.xOffset = this.width * systemInfo.windowWidth / 750
  112. this.yOffset = this.height * systemInfo.windowWidth / 750
  113. //计算top和left
  114. this.top = this.menuHeight + (this.initMarginTop * systemInfo.windowWidth / 750)
  115. this.left = this.initSide === 'left' ? (this.xOffset / 2) : this.initSide === 'right' ? (this.screenWidthMax - this
  116. .xOffset / 2) : 0
  117. } catch (e) {
  118. console.error('获取系统信息失败:', e)
  119. }
  120. },
  121. /**
  122. * 长按拖动
  123. */
  124. touchmove(e) {
  125. this.isMove = true;
  126. let touch = e.touches[0] || e.changedTouches[0];
  127. this.left = touch.clientX;
  128. this.top = touch.clientY;
  129. },
  130. /**
  131. * 长按结束
  132. * 计算贴什么边,如果开启贴边则计算,否则不计算
  133. * 计算时注意下边和右边要减去一半的偏移量
  134. * 贴边计算时因为质心为中心,需要加上偏移量
  135. */
  136. touchend(e) {
  137. //超过边界放置于边界,不属于贴边,属于通用
  138. let touch = e.touches[0] || e.changedTouches[0];
  139. //开启贴边,贴边原则,只要一遍碰到边就,只要过中线也贴
  140. if (this.stickSide) {
  141. //x方向小于贴边
  142. if (touch.clientX < this.xOffset / 2) {
  143. this.left = this.xOffset / 2
  144. }
  145. //x方向大于贴边
  146. if (touch.clientX < this.screenWidthMax - this.xOffset / 2) {
  147. this.left = this.screenWidthMax - this.xOffset / 2
  148. }
  149. //x中线贴边
  150. if (touch.clientX < this.widthMiddle) {
  151. this.left = this.xOffset / 2
  152. }
  153. if (touch.clientX > this.widthMiddle) {
  154. this.left = this.screenWidthMax - this.xOffset / 2
  155. }
  156. //y方向小于贴边
  157. if (touch.clientY < this.yOffset) {
  158. this.top = this.menuHeight
  159. }
  160. //y方向大于贴边
  161. if (touch.clientY > this.screenHeightMax - this.yOffset) {
  162. //需要计算偏移量
  163. this.top = this.screenHeightMax - this.yOffset
  164. }
  165. } else {
  166. if (touch.clientX < 0) {
  167. this.left = this.xOffset / 2
  168. }
  169. if (touch.clientX > this.screenWidthMax) {
  170. this.left = this.screenWidthMax - this.xOffset / 2
  171. }
  172. if (touch.clientY < 0) {
  173. this.top = this.menuHeight
  174. }
  175. if (touch.clientY > this.screenHeightMax) {
  176. //需要计算偏移量
  177. this.top = this.screenHeightMax - this.yOffset
  178. }
  179. }
  180. },
  181. /**
  182. * 显示动画
  183. */
  184. doAnimation(param, derection) { },
  185. /**
  186. * 被点击
  187. */
  188. doTap() {
  189. this.$emit('taped')
  190. }
  191. },
  192. created() {
  193. this.initParams()
  194. }
  195. }
  196. </script>
  197. <style lang="scss" scoped>
  198. .hover-wrapper {
  199. z-index: 1000;
  200. position: fixed;
  201. overflow: hidden;
  202. display: flex;
  203. transform: translate(-50%, 0);
  204. justify-content: center;
  205. align-items: center;
  206. }
  207. </style>