index.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. <!--
  2. sx-rate uni-app滑选星星组件
  3. 版本 1.1.2
  4. 日期 2020-6-4
  5. 作者 sunxi1997
  6. 链接 https://ext.dcloud.net.cn/plugin?id=1027
  7. -->
  8. <template>
  9. <view
  10. class="rate-box"
  11. :class="[{animation},containerClass]"
  12. @touchmove="ontouchmove"
  13. @touchend="touchMoving=false"
  14. >
  15. <view
  16. v-for="(val,i) in list"
  17. :key="val"
  18. class="rate"
  19. :style="{fontSize, paddingLeft: i!==0 ? rateMargin : 0, paddingRight: i<list.length-1 ? rateMargin : 0, color: val<=rateValue ? activeColor : defaultColor}"
  20. :class="[{scale: !disabled && val<=rateValue && animation && touchMoving},`rate-${i}`,rateClass]"
  21. :data-val="val"
  22. @click="onItemClick"
  23. >
  24. <text class="iconfont icon-star" />
  25. </view>
  26. </view>
  27. </template>
  28. <script>
  29. import { getClientRect } from "./common";
  30. export default {
  31. name: 'sx-rate',
  32. props: {
  33. // 当前值
  34. value: {
  35. type: Number,
  36. default: 3
  37. },
  38. // 最大星星数量
  39. max: {
  40. type: Number,
  41. default: 5
  42. },
  43. // 禁用
  44. disabled: {
  45. type: Boolean,
  46. default: false
  47. },
  48. // 动画效果
  49. animation: {
  50. type: Boolean,
  51. default: true
  52. },
  53. // 默认星星颜色
  54. defaultColor: {
  55. type: String,
  56. default: '#ccc'
  57. },
  58. // 滑选后星星颜色
  59. activeColor: {
  60. type: String,
  61. default: '#FFB700'
  62. },
  63. // 星星大小
  64. fontSize: {
  65. type: String,
  66. default: 'inherit'
  67. },
  68. // 星星间距
  69. margin: {
  70. type: String,
  71. default: ''
  72. },
  73. // 自定义类名-容器
  74. containerClass: {
  75. type: String,
  76. default: ''
  77. },
  78. // 自定义类名-星星
  79. rateClass: {
  80. type: String,
  81. default: ''
  82. }
  83. },
  84. data() {
  85. return {
  86. rateValue: 0,
  87. touchMoving: false,
  88. startX: [],
  89. startW: 30
  90. }
  91. },
  92. computed: {
  93. list() {
  94. return [...new Array(this.max)].map((_, i) => i + 1)
  95. },
  96. rateMargin() {
  97. let margin = this.margin;
  98. if (!margin)
  99. return 0;
  100. switch (typeof margin) {
  101. case "number":
  102. margin += 'px';
  103. case "string":
  104. break;
  105. default:
  106. return 0;
  107. }
  108. let reg = /^(\d+)([^\d]*)/;
  109. let result = reg.exec(margin);
  110. if (!result)
  111. return 0;
  112. let [_, num, unit] = result;
  113. return num / 2 + unit;
  114. }
  115. },
  116. watch: {
  117. value: {
  118. handler(val) {
  119. this.rateValue = val;
  120. },
  121. immediate: true
  122. }
  123. },
  124. methods: {
  125. // 计算星星位置
  126. async initStartX() {
  127. let {max} = this;
  128. this.startX = [];
  129. for (let i = 0; i < max; i++) {
  130. let selector = `.rate-${ i }`;
  131. let {left, width} = await getClientRect(selector, this);
  132. this.startX.push(left);
  133. this.startW = width;
  134. }
  135. },
  136. /**
  137. * 手指滑动事件回调
  138. * https://github.com/sunxi1997/uni-app-sx-rate/pull/1
  139. * 原本的触摸处理在自定了样式后可能会出现bug, 已解决
  140. */
  141. async ontouchmove(e) {
  142. if (!this.touchMoving) {
  143. this.touchMoving = true;
  144. // 开始手指滑动时重新计算星星位置,防止星星位置意外变化
  145. await this.initStartX();
  146. }
  147. let {startX, startW, max} = this;
  148. let {touches} = e;
  149. // 触摸焦点停留的位置
  150. let {pageX} = touches[touches.length - 1];
  151. // 超出最左边, 0 星
  152. if (pageX <= startX[0])
  153. return this.toggle(0);
  154. // 刚好在第一颗星
  155. else if (pageX <= startX[0] + startW)
  156. return this.toggle(1);
  157. // 超出最右边, 最大星
  158. else if (pageX >= startX[max - 1])
  159. return this.toggle(max);
  160. //计算星星停留的位置
  161. let startXHash = startX.concat(pageX).sort((a, b) => a - b);
  162. this.toggle(startXHash.indexOf(pageX));
  163. },
  164. // 点击回调
  165. onItemClick(e) {
  166. let {val} = e.currentTarget.dataset;
  167. this.toggle(+val)
  168. },
  169. // 修改值
  170. toggle(val) {
  171. let {disabled} = this;
  172. val = +val;
  173. if (disabled || isNaN(val))
  174. return;
  175. if (this.rateValue !== val) {
  176. this.rateValue = val;
  177. this.$emit('update:value', val);
  178. this.$emit('change', val)
  179. }
  180. }
  181. },
  182. }
  183. </script>
  184. <style scoped>
  185. @import "../../static/sx-rate/iconfont.css";
  186. </style>
  187. <style scoped>
  188. .rate-box {
  189. min-height: 1.4em;
  190. display: flex;
  191. align-items: center;
  192. }
  193. .rate {
  194. display: inline-flex;
  195. justify-content: center;
  196. align-items: center;
  197. width: 1.2em;
  198. transition: all .15s linear;
  199. }
  200. .rate.scale {
  201. transform: scale(1.1);
  202. }
  203. </style>