u-button.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. <template>
  2. <button
  3. id="u-wave-btn"
  4. class="u-btn u-line-1 u-fix-ios-appearance"
  5. :class="[
  6. 'u-size-' + size,
  7. plain ? 'u-' + type + '-plain' : '',
  8. loading ? 'u-loading' : '',
  9. shape == 'circle' ? 'u-round-circle' : '',
  10. hairLine ? showHairLineBorder : 'u-bold-border'
  11. ]"
  12. :disabled="disabled"
  13. :form-type="formType"
  14. :open-type="openType"
  15. :app-parameter="appParameter"
  16. :hover-stop-propagation="hoverStopPropagation"
  17. :send-message-title="sendMessageTitle"
  18. send-message-path="sendMessagePath"
  19. :lang="lang"
  20. :session-from="sessionFrom"
  21. :send-message-img="sendMessageImg"
  22. :show-message-card="showMessageCard"
  23. @getphonenumber="getphonenumber"
  24. @getuserinfo="getuserinfo"
  25. @error="error"
  26. @opensetting="opensetting"
  27. @launchapp="launchapp"
  28. :style="[buttonStyle]"
  29. @tap.stop="click($event)"
  30. :hover-class="getHoverClass"
  31. :loading="loading"
  32. >
  33. <slot></slot>
  34. <view
  35. v-if="ripple"
  36. class="u-wave-ripple"
  37. :class="[waveActive ? 'u-wave-active' : '']"
  38. :style="{
  39. top: rippleTop + 'px',
  40. left: rippleLeft + 'px',
  41. width: fields.targetWidth + 'px',
  42. height: fields.targetWidth + 'px',
  43. 'background-color': rippleBgColor || 'rgba(0, 0, 0, 0.15)'
  44. }"
  45. ></view>
  46. </button>
  47. </template>
  48. <script>
  49. /**
  50. * button 按钮
  51. * @description Button 按钮
  52. * @tutorial https://www.uviewui.com/components/button.html
  53. * @property {String} size 按钮的大小
  54. * @property {Boolean} ripple 是否开启点击水波纹效果
  55. * @property {String} ripple-bg-color 水波纹的背景色,ripple为true时有效
  56. * @property {String} type 按钮的样式类型
  57. * @property {Boolean} plain 按钮是否镂空,背景色透明
  58. * @property {Boolean} disabled 是否禁用
  59. * @property {Boolean} hair-line 是否显示按钮的细边框(默认true)
  60. * @property {Boolean} shape 按钮外观形状,见文档说明
  61. * @property {Boolean} loading 按钮名称前是否带 loading 图标(App-nvue 平台,在 ios 上为雪花,Android上为圆圈)
  62. * @property {String} form-type 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
  63. * @property {String} open-type 开放能力
  64. * @property {String} hover-class 指定按钮按下去的样式类。当 hover-class="none" 时,没有点击态效果(App-nvue 平台暂不支持)
  65. * @property {Number} hover-start-time 按住后多久出现点击态,单位毫秒
  66. * @property {Number} hover-stay-time 手指松开后点击态保留时间,单位毫秒
  67. * @property {Object} custom-style 对按钮的自定义样式,对象形式,见文档说明
  68. * @event {Function} click 按钮点击
  69. * @event {Function} getphonenumber open-type="getPhoneNumber"时有效
  70. * @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,从返回参数的detail中获取到的值同uni.getUserInfo
  71. * @event {Function} error 当使用开放能力时,发生错误的回调
  72. * @event {Function} opensetting 在打开授权设置页并关闭后回调
  73. * @event {Function} launchapp 打开 APP 成功的回调
  74. * @example <u-button>月落</u-button>
  75. */
  76. export default {
  77. name: 'u-button',
  78. props: {
  79. // 是否细边框
  80. hairLine: {
  81. type: Boolean,
  82. default: true
  83. },
  84. // 按钮的预置样式,default,primary,error,warning,success
  85. type: {
  86. type: String,
  87. default: 'default'
  88. },
  89. // 按钮尺寸,default,medium,mini
  90. size: {
  91. type: String,
  92. default: 'default'
  93. },
  94. // 按钮形状,circle(两边为半圆),square(带圆角)
  95. shape: {
  96. type: String,
  97. default: 'square'
  98. },
  99. // 按钮是否镂空
  100. plain: {
  101. type: Boolean,
  102. default: false
  103. },
  104. // 是否禁止状态
  105. disabled: {
  106. type: Boolean,
  107. default: false
  108. },
  109. // 是否加载中
  110. loading: {
  111. type: Boolean,
  112. default: false
  113. },
  114. // 开放能力,具体请看uniapp稳定关于button组件部分说明
  115. // https://uniapp.dcloud.io/component/button
  116. openType: {
  117. type: String,
  118. default: ''
  119. },
  120. // 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
  121. // 取值为submit(提交表单),reset(重置表单)
  122. formType: {
  123. type: String,
  124. default: ''
  125. },
  126. // 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效
  127. // 只微信小程序、QQ小程序有效
  128. appParameter: {
  129. type: String,
  130. default: ''
  131. },
  132. // 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效
  133. hoverStopPropagation: {
  134. type: Boolean,
  135. default: false
  136. },
  137. // 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。只微信小程序有效
  138. lang: {
  139. type: String,
  140. default: 'en'
  141. },
  142. // 会话来源,open-type="contact"时有效。只微信小程序有效
  143. sessionFrom: {
  144. type: String,
  145. default: ''
  146. },
  147. // 会话内消息卡片标题,open-type="contact"时有效
  148. // 默认当前标题,只微信小程序有效
  149. sendMessageTitle: {
  150. type: String,
  151. default: ''
  152. },
  153. // 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效
  154. // 默认当前分享路径,只微信小程序有效
  155. sendMessagePath: {
  156. type: String,
  157. default: ''
  158. },
  159. // 会话内消息卡片图片,open-type="contact"时有效
  160. // 默认当前页面截图,只微信小程序有效
  161. sendMessageImg: {
  162. type: String,
  163. default: ''
  164. },
  165. // 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,
  166. // 用户点击后可以快速发送小程序消息,open-type="contact"时有效
  167. showMessageCard: {
  168. type: Boolean,
  169. default: false
  170. },
  171. // 手指按(触摸)按钮时按钮时的背景颜色
  172. hoverBgColor: {
  173. type: String,
  174. default: ''
  175. },
  176. // 水波纹的背景颜色
  177. rippleBgColor: {
  178. type: String,
  179. default: ''
  180. },
  181. // 是否开启水波纹效果
  182. ripple: {
  183. type: Boolean,
  184. default: false
  185. },
  186. // 按下的类名
  187. hoverClass: {
  188. type: String,
  189. default: ''
  190. },
  191. // 自定义样式,对象形式
  192. customStyle: {
  193. type: Object,
  194. default() {
  195. return {};
  196. }
  197. }
  198. },
  199. computed: {
  200. // 当没有传bgColor变量时,按钮按下去的颜色类名
  201. getHoverClass() {
  202. // 如果开启水波纹效果,则不启用hover-class效果
  203. if (this.loading || this.disabled || this.ripple || this.hoverClass) return '';
  204. let hoverClass = '';
  205. hoverClass = this.plain ? 'u-' + this.type + '-plain-hover' : 'u-' + this.type + '-hover';
  206. return hoverClass;
  207. },
  208. // 按钮主题
  209. buttonStyle() {
  210. let style = {};
  211. if (this.type == 'default') {
  212. if (this.disabled) {
  213. style.color = '#c0c4cc';
  214. style.backgroundColor = '#ffffff';
  215. style.borderColor = '#e4e7ed';
  216. } else {
  217. style.color = this.$u.color['contentColor'];
  218. style.backgroundColor = '#ffffff';
  219. style.borderColor = '#c0c4cc';
  220. }
  221. } else {
  222. if (this.disabled) {
  223. if (this.plain) {
  224. style.color = this.$u.color[this.type + 'Disabled'];
  225. style.backgroundColor = this.$u.color[this.type + 'Light'];
  226. style.borderColor = this.$u.color[this.type + 'Disabled'];
  227. } else {
  228. style.color = '#ffffff';
  229. style.backgroundColor = this.$u.color[this.type + 'Disabled'];
  230. style.borderColor = this.$u.color[this.type + 'Disabled'];
  231. }
  232. } else {
  233. if (this.plain) {
  234. style.color = this.$u.color[this.type];
  235. style.backgroundColor = this.$u.color[this.type + 'Light'];
  236. style.borderColor = this.$u.color[this.type + 'Disabled'];
  237. } else {
  238. style.color = '#ffffff';
  239. style.backgroundColor = this.$u.color[this.type];
  240. style.borderColor = this.$u.color[this.type];
  241. }
  242. }
  243. }
  244. return Object.assign(style, this.customStyle);
  245. },
  246. // 在'primary', 'success', 'error', 'warning'类型下,不显示边框,否则会造成四角有毛刺现象
  247. showHairLineBorder() {
  248. if (['primary', 'success', 'error', 'warning'].indexOf(this.type) >= 0 && !this.plain) {
  249. return '';
  250. } else {
  251. return 'u-hairline-border';
  252. }
  253. }
  254. },
  255. data() {
  256. return {
  257. rippleTop: 0, // 水波纹的起点Y坐标到按钮上边界的距离
  258. rippleLeft: 0, // 水波纹起点X坐标到按钮左边界的距离
  259. fields: {}, // 波纹按钮节点信息
  260. waveActive: false // 激活水波纹
  261. };
  262. },
  263. methods: {
  264. // 按钮点击
  265. click(e) {
  266. // 如果按钮时disabled和loading状态,不触发水波纹效果
  267. if (this.loading === true || this.disabled === true) return;
  268. // 是否开启水波纹效果
  269. if (this.ripple) {
  270. // 每次点击时,移除上一次的类,再次添加,才能触发动画效果
  271. this.waveActive = false;
  272. this.$nextTick(function() {
  273. this.getWaveQuery(e);
  274. });
  275. }
  276. this.$emit('click');
  277. },
  278. // 查询按钮的节点信息
  279. getWaveQuery(e) {
  280. this.getElQuery().then(res => {
  281. // 查询返回的是一个数组节点
  282. let data = res[0];
  283. // 查询不到节点信息,不操作
  284. if (!data.width || !data.width) return;
  285. // 水波纹的最终形态是一个正方形(通过border-radius让其变为一个圆形),这里要保证正方形的边长等于按钮的最长边
  286. // 最终的方形(变换后的圆形)才能覆盖整个按钮
  287. data.targetWidth = data.height > data.width ? data.height : data.width;
  288. if (!data.targetWidth) return;
  289. this.fields = data;
  290. let touchesX = '',
  291. touchesY = '';
  292. // #ifdef MP-BAIDU
  293. touchesX = e.changedTouches[0].clientX;
  294. touchesY = e.changedTouches[0].clientY;
  295. // #endif
  296. // #ifdef MP-ALIPAY
  297. touchesX = e.detail.clientX;
  298. touchesY = e.detail.clientY;
  299. // #endif
  300. // #ifndef MP-BAIDU || MP-ALIPAY
  301. touchesX = e.touches[0].clientX;
  302. touchesY = e.touches[0].clientY;
  303. // #endif
  304. // 获取触摸点相对于按钮上边和左边的x和y坐标,原理是通过屏幕的触摸点(touchesY),减去按钮的上边界data.top
  305. // 但是由于`transform-origin`默认是center,所以这里再减去半径才是水波纹view应该的位置
  306. // 总的来说,就是把水波纹的矩形(变换后的圆形)的中心点,移动到我们的触摸点位置
  307. this.rippleTop = touchesY - data.top - data.targetWidth / 2;
  308. this.rippleLeft = touchesX - data.left - data.targetWidth / 2;
  309. this.$nextTick(() => {
  310. this.waveActive = true;
  311. });
  312. });
  313. },
  314. // 获取节点信息
  315. getElQuery() {
  316. return new Promise(resolve => {
  317. let queryInfo = '';
  318. // 获取元素节点信息,请查看uniapp相关文档
  319. // https://uniapp.dcloud.io/api/ui/nodes-info?id=nodesrefboundingclientrect
  320. queryInfo = uni.createSelectorQuery().in(this);
  321. //#ifdef MP-ALIPAY
  322. queryInfo = uni.createSelectorQuery();
  323. //#endif
  324. queryInfo.select('.u-btn').boundingClientRect();
  325. queryInfo.exec(data => {
  326. resolve(data);
  327. });
  328. });
  329. },
  330. // 下面为对接uniapp官方按钮开放能力事件回调的对接
  331. getphonenumber(res) {
  332. this.$emit('getphonenumber', res);
  333. },
  334. getuserinfo(res) {
  335. this.$emit('getuserinfo', res);
  336. },
  337. error(res) {
  338. this.$emit('error', res);
  339. },
  340. opensetting(res) {
  341. this.$emit('opensetting', res);
  342. },
  343. launchapp(res) {
  344. this.$emit('launchapp', res);
  345. }
  346. }
  347. };
  348. </script>
  349. <style scoped lang="scss">
  350. .u-btn::after {
  351. border: none;
  352. }
  353. .u-btn {
  354. position: relative;
  355. border: 0;
  356. //border-radius: 10rpx;
  357. display: inline-block;
  358. overflow: hidden;
  359. line-height: 1;
  360. display: flex;
  361. align-items: center;
  362. justify-content: center;
  363. cursor: pointer;
  364. padding: 0 40rpx;
  365. z-index: 1;
  366. box-sizing: border-box;
  367. transition: all 0.15s;
  368. }
  369. .u-hairline-border:after {
  370. content: ' ';
  371. position: absolute;
  372. pointer-events: none;
  373. // 设置为border-box,意味着下面的scale缩小为0.5,实际上缩小的是伪元素的内容(border-box意味着内容不含border)
  374. box-sizing: border-box;
  375. // 中心点作为变形(scale())的原点
  376. -webkit-transform-origin: 0 0;
  377. transform-origin: 0 0;
  378. left: 0;
  379. top: 0;
  380. width: 199.8%;
  381. height: 199.7%;
  382. -webkit-transform: scale(0.5, 0.5);
  383. transform: scale(0.5, 0.5);
  384. border: 1px solid currentColor;
  385. z-index: 1;
  386. }
  387. .u-bold-border {
  388. border: 1px solid #ffffff;
  389. }
  390. .u-wave-ripple {
  391. z-index: 0;
  392. position: absolute;
  393. border-radius: 100%;
  394. background-clip: padding-box;
  395. pointer-events: none;
  396. user-select: none;
  397. transform: scale(0);
  398. opacity: 1;
  399. transform-origin: center;
  400. }
  401. .u-wave-ripple.u-wave-active {
  402. opacity: 0;
  403. transform: scale(2);
  404. transition: opacity 1s linear, transform 0.4s linear;
  405. }
  406. .u-round-circle {
  407. border-radius: 100rpx;
  408. }
  409. .u-round-circle::after {
  410. border-radius: 100rpx;
  411. }
  412. .u-loading::after {
  413. background-color: hsla(0, 0%, 100%, 0.35);
  414. }
  415. .u-size-default {
  416. font-size: 30rpx;
  417. height: 80rpx;
  418. line-height: 80rpx;
  419. }
  420. .u-size-medium {
  421. display: inline-flex;
  422. width: auto;
  423. font-size: 26rpx;
  424. height: 70rpx;
  425. line-height: 70rpx;
  426. padding: 0 80rpx;
  427. }
  428. .u-size-mini {
  429. display: inline-flex;
  430. width: auto;
  431. font-size: 22rpx;
  432. padding-top: 1px;
  433. height: 50rpx;
  434. line-height: 50rpx;
  435. padding: 0 20rpx;
  436. }
  437. .u-primary-plain-hover {
  438. color: #ffffff !important;
  439. background: $u-type-primary-dark !important;
  440. }
  441. .u-default-plain-hover {
  442. color: $u-type-primary-dark !important;
  443. background: $u-type-primary-light !important;
  444. }
  445. .u-success-plain-hover {
  446. color: #ffffff !important;
  447. background: $u-type-success-dark !important;
  448. }
  449. .u-warning-plain-hover {
  450. color: #ffffff !important;
  451. background: $u-type-warning-dark !important;
  452. }
  453. .u-error-plain-hover {
  454. color: #ffffff !important;
  455. background: $u-type-error-dark !important;
  456. }
  457. .u-info-plain-hover {
  458. color: #ffffff !important;
  459. background: $u-type-info-dark !important;
  460. }
  461. .u-default-hover {
  462. color: $u-type-primary-dark !important;
  463. border-color: $u-type-primary-dark !important;
  464. background-color: $u-type-primary-light !important;
  465. }
  466. .u-primary-hover {
  467. background: $u-type-primary-dark !important;
  468. color: #fff;
  469. }
  470. .u-success-hover {
  471. background: $u-type-success-dark !important;
  472. color: #fff;
  473. }
  474. .u-info-hover {
  475. background: $u-type-info-dark !important;
  476. color: #fff;
  477. }
  478. .u-warning-hover {
  479. background: $u-type-warning-dark !important;
  480. color: #fff;
  481. }
  482. .u-error-hover {
  483. background: $u-type-error-dark !important;
  484. color: #fff;
  485. }
  486. </style>