index.vue 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. <template>
  2. <view class="px-suspen-button" :style="{'top':elTop,'left':elLeft,'zIndex':zindex}"
  3. @touchmove.stop.prevent="onTouchMove">
  4. <view :class="['center-button','px-button',{'active':open}]" @click.stop="onOpen"
  5. :style="{'backgroundColor':bgColor,'height':_size,'width':_size,'zIndex':zindex+1,'opacity':opacity}">
  6. <image v-if="!$slots.center" mode="widthFix" :style="{'width':_iconSize}"
  7. :src="centerIcon||defaultCenterIcon">
  8. </image>
  9. <slot name="center"></slot>
  10. </view>
  11. <view :class="['top-button','px-button','ot-button',{'active-top':open,'hidden-top':!first&&!open}]"
  12. :style="{'backgroundColor':bgColor,'height':_size,'width':_size,'zIndex':zindex}" @click.stop="onClickTop">
  13. <image v-if="!$slots.top" mode="widthFix" :style="{'width':_iconSize}" :src="topIcon||defaultTopIcon">
  14. </image>
  15. <slot name="top"></slot>
  16. </view>
  17. <view :class="['left-button','px-button','ot-button',{'active-left':open,'hidden-left':!first&&!open}]"
  18. :style="{'backgroundColor':bgColor,'height':_size,'width':_size,'zIndex':zindex}" @click.stop="onClickLeft">
  19. <image v-if="!$slots.left" mode="widthFix" :style="{'width':_iconSize}" :src="leftIcon||defaultLeftIcon">
  20. </image>
  21. <slot name="left"></slot>
  22. </view>
  23. <view :class="['bottom-button','px-button','ot-button',{'active-bottom':open,'hidden-bottom':!first&&!open}]"
  24. :style="{'backgroundColor':bgColor,'height':_size,'width':_size,'zIndex':zindex}"
  25. @click.stop="onClickBottom">
  26. <image v-if="!$slots.bottom" mode="widthFix" :style="{'width':_iconSize}"
  27. :src="bottomIcon||defaultBottomIcon">
  28. </image>
  29. <slot name="bottom"></slot>
  30. </view>
  31. </view>
  32. </template>
  33. <script>
  34. import {
  35. centerIcon as defaultCenterIcon,
  36. leftIcon as defaultLeftIcon,
  37. topIcon as defaultTopIcon,
  38. bottomIcon as defaultBottomIcon
  39. } from './iconBase64.js'
  40. export default {
  41. props: {
  42. top: {
  43. type: [Number, String],
  44. default: "70%"
  45. },
  46. left: {
  47. type: [Number, String],
  48. default: "85%"
  49. },
  50. bgColor: { //按钮底色
  51. type: String,
  52. default: "rgb(235, 155, 50)"
  53. },
  54. size: { //按钮直径大小
  55. type: [Number, String],
  56. default: 80
  57. },
  58. opacity: {
  59. type: Number,
  60. default: 0.8
  61. },
  62. move: { //可移动
  63. type: Boolean,
  64. default: true
  65. },
  66. centerIcon: {
  67. type: String,
  68. default: ""
  69. },
  70. topIcon: {
  71. type: String,
  72. default: ""
  73. },
  74. leftIcon: {
  75. type: String,
  76. default: ""
  77. },
  78. bottomIcon: {
  79. type: String,
  80. default: ""
  81. },
  82. zindex: {
  83. type: Number,
  84. default: 999
  85. },
  86. iconSize: { //图标尺寸
  87. type: [Number, String],
  88. default: '100%'
  89. },
  90. turn: { //是否可以展开
  91. type: Boolean,
  92. default: true
  93. }
  94. },
  95. data() {
  96. return {
  97. first: true,
  98. open: false,
  99. elTop: '',
  100. elLeft: '',
  101. _size: '',
  102. _iconSize: '',
  103. defaultCenterIcon,
  104. defaultLeftIcon,
  105. defaultTopIcon,
  106. defaultBottomIcon,
  107. windowHeight: '',
  108. windowWidth: '',
  109. rate: '',
  110. //#ifdef H5||APP
  111. windowTop: '',
  112. //#endif
  113. }
  114. },
  115. watch: {
  116. top: {
  117. handler(newval) {
  118. this.elTop = this.unitCheck(newval);
  119. },
  120. immediate: true
  121. },
  122. left: {
  123. handler(newval) {
  124. this.elLeft = this.unitCheck(newval);
  125. },
  126. immediate: true
  127. },
  128. size: {
  129. handler(newval) {
  130. this._size = this.unitCheck(newval);
  131. },
  132. immediate: true
  133. },
  134. iconSize: {
  135. handler(newval) {
  136. this._iconSize = this.unitCheck(newval);
  137. },
  138. immediate: true
  139. },
  140. },
  141. mounted() {
  142. uni.getSystemInfo({
  143. success: res => {
  144. let width = res.windowWidth;
  145. let rate = 750.00 / width;
  146. this.windowHeight = res.windowHeight;
  147. this.windowWidth = width;
  148. this.rate = rate;
  149. //#ifdef H5||APP
  150. this.windowTop = res.windowTop;
  151. //#endif
  152. },
  153. });
  154. },
  155. methods: {
  156. unitCheck(value) {
  157. const val = String(value);
  158. if (!val.includes('px') && !val.includes('%')) {
  159. return `${val}rpx`;
  160. }
  161. return val;
  162. },
  163. onOpen() {
  164. if (this.turn) {
  165. this.open = !this.open;
  166. this.first = false;
  167. this.$emit( this.open ? 'open':'close')
  168. }
  169. else{
  170. this.$emit('click')
  171. }
  172. },
  173. onTouchMove(e) {
  174. if (!this.move) {
  175. return
  176. }
  177. let x = e.touches[0].clientX;
  178. let y = e.touches[0].clientY;
  179. if (x < 0) {
  180. x = 0;
  181. } else if (x > (this.windowWidth - this.size / this.rate)) {
  182. x = this.windowWidth - this.size / this.rate;
  183. }
  184. //#ifndef H5||APP-PLUS
  185. if (y < 0) {
  186. y = 0;
  187. } else if (y > (this.windowHeight - this.size / this.rate)) {
  188. y = this.windowHeight - this.size / this.rate;
  189. }
  190. //#endif
  191. //#ifdef H5||APP-PLUS
  192. if (y < this.windowTop) {
  193. y = this.windowTop;
  194. } else if (y > (this.windowHeight - this.size / this.rate + this.windowTop)) {
  195. y = this.windowHeight - this.size / this.rate + this.windowTop;
  196. }
  197. //#endif
  198. this.elLeft = x + "px";
  199. this.elTop = y + "px";
  200. },
  201. onClickTop() {
  202. this.$emit('top')
  203. },
  204. onClickLeft() {
  205. this.$emit('left')
  206. },
  207. onClickBottom() {
  208. this.$emit('bottom')
  209. }
  210. }
  211. }
  212. </script>
  213. <style lang="scss" scoped>
  214. .px-suspen-button {
  215. position: fixed;
  216. z-index: 999;
  217. }
  218. .px-button {
  219. position: absolute;
  220. border-radius: 50%;
  221. top: 0rpx;
  222. left: 0rpx;
  223. height: 80rpx;
  224. width: 80rpx;
  225. background: rgb(235, 155, 50);
  226. display: flex;
  227. flex-direction: column;
  228. justify-content: center;
  229. align-items: center;
  230. opacity: 0.8;
  231. z-index: 999;
  232. overflow: hidden;
  233. &.center-button {
  234. z-index: 1000;
  235. }
  236. &.active {
  237. animation: 0.5s ease center forwards;
  238. }
  239. &.ot-button {
  240. display: none;
  241. opacity: 0;
  242. &.active-top {
  243. animation: 0.5s ease top forwards;
  244. display: flex;
  245. }
  246. &.hidden-top {
  247. animation: 0.5s ease hidetop forwards;
  248. display: flex;
  249. }
  250. &.active-left {
  251. animation: 0.5s ease left forwards;
  252. display: flex;
  253. }
  254. &.hidden-left {
  255. animation: 0.5s ease hideleft forwards;
  256. display: flex;
  257. }
  258. &.active-bottom {
  259. animation: 0.5s ease bottom forwards;
  260. display: flex;
  261. }
  262. &.hidden-bottom {
  263. animation: 0.5s ease hidebottom forwards;
  264. display: flex;
  265. }
  266. }
  267. }
  268. @keyframes center {
  269. 0% {}
  270. 100% {
  271. transform: rotate(315deg);
  272. opacity: 1
  273. }
  274. }
  275. @keyframes bottom {
  276. 0% {
  277. opacity: 0
  278. }
  279. 100% {
  280. top: 100rpx;
  281. left: -120rpx;
  282. transform: rotate(360deg);
  283. opacity: 1
  284. }
  285. }
  286. @keyframes left {
  287. 0% {
  288. opacity: 0
  289. }
  290. 100% {
  291. top: 0rpx;
  292. left: -180rpx;
  293. transform: rotate(360deg);
  294. opacity: 1
  295. }
  296. }
  297. @keyframes top {
  298. 0% {
  299. opacity: 0
  300. }
  301. 100% {
  302. top: -100rpx;
  303. left: -120rpx;
  304. transform: rotate(360deg);
  305. opacity: 1
  306. }
  307. }
  308. @keyframes hideleft {
  309. 0% {
  310. top: 0rpx;
  311. left: -180rpx;
  312. opacity: 1
  313. }
  314. 100% {
  315. top: 0rpx;
  316. left: 0rpx;
  317. transform: rotate(360deg);
  318. opacity: 0
  319. }
  320. }
  321. @keyframes hidebottom {
  322. 0% {
  323. top: 100rpx;
  324. left: -120rpx;
  325. opacity: 1
  326. }
  327. 100% {
  328. top: 0rpx;
  329. left: 0rpx;
  330. transform: rotate(360deg);
  331. opacity: 0
  332. }
  333. }
  334. @keyframes hidetop {
  335. 0% {
  336. top: -100rpx;
  337. left: -120rpx;
  338. opacity: 1
  339. }
  340. 100% {
  341. top: 0rpx;
  342. left: 0rpx;
  343. transform: rotate(360deg);
  344. opacity: 0
  345. }
  346. }
  347. </style>