DoubleSlider.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. <template>
  2. <view class="double-slider" :style="{
  3. width: `calc(${sliderWidth} - ${blockSize})`,
  4. height: sliderHeight,//`calc(${sliderHeight}+3*${borderRadius})`,
  5. background: `${indicatorColor}`,
  6. borderRadius: `${radius}`,
  7. margin: `calc(${blockSize} / 2) 0`,
  8. }">
  9. <view class="active-slider" :style="{
  10. width: `${xWidth - activeX}px`,
  11. height: sliderHeight,//`calc(${sliderHeight}+3*${borderRadius})`,
  12. background: `${disabled || (leftBlockDisabled && rightBlockDisabled)? disabledIndicatorColor:activeColor}`,
  13. transform: `translateX(${activeX}px)`,
  14. left: `calc(${blockSize} / 2)`
  15. }">
  16. <view class="block1" :style="[getLeftBlockStyle]" @touchstart="blockStart1"
  17. @touchmove.stop.parevent="blockMove1">
  18. <label style="position: relative;line-height:28px; " :style="[getLableStyle]">{{currentMinValue}}</label>
  19. </view>
  20. <view class="block2" :style="[getRightBlockStyle]" @touchstart="blockStart2"
  21. @touchmove.stop.parevent="blockMove2" @touchend="isLeft = false">
  22. <view style="position: relative;line-height:28px;" :style="[getLableStyle]" >
  23. <label >{{currentMaxValue}}</label>
  24. </view>
  25. </view>
  26. </view>
  27. </view>
  28. </template>
  29. <script>
  30. export default {
  31. props: {
  32. labelColor:{
  33. type:String,
  34. default:'#000000'
  35. },
  36. sliderExtendHeight:{
  37. type:String,
  38. default:'30rpx'
  39. },
  40. sliderWidth: {
  41. type: String,
  42. default: "100%"
  43. },
  44. sliderHeight: {
  45. type: String,
  46. default: "15rpx"
  47. },
  48. indicatorColor: {
  49. type: String,
  50. default: "#FF0000"//"#EFEFEF"
  51. },
  52. activeColor: {
  53. type: String,
  54. default: "skyblue"
  55. },
  56. borderStyle:{
  57. type: String,
  58. default: "solid"
  59. },
  60. borderColor:{
  61. type:String,
  62. default: "#00B962"
  63. },
  64. borderWidth:{
  65. type:String,
  66. default:'1rpx'
  67. },
  68. borderRadius:{
  69. type: String,
  70. default:'20rpx'
  71. },
  72. // style="border-radius: 25rpx;border-style:solid ; border-color: blue;border-width: 1rpx; "
  73. radius: {
  74. type: String,
  75. default: "20rpx"
  76. },
  77. blockSize: {
  78. type: String,
  79. default: "36rpx"
  80. },
  81. blockColor: {
  82. type: String,
  83. default: "#3489F8"
  84. },
  85. /*
  86. currentValue: {
  87. type: Array,
  88. default: [20, 80]
  89. },*/
  90. currentMinValue:{
  91. type:Number,
  92. default:0
  93. },
  94. currentMaxValue:{
  95. type:Number,
  96. defult:100
  97. },
  98. minValue: {
  99. type: Number,
  100. default: 0
  101. },
  102. maxValue: {
  103. type: Number,
  104. default: 100
  105. },
  106. step: {
  107. type: Number,
  108. default: 0
  109. },
  110. disabled: {
  111. type: Boolean,
  112. default: false
  113. },
  114. leftBlockDisabled: {
  115. type: Boolean,
  116. default: false
  117. },
  118. rightBlockDisabled: {
  119. type: Boolean,
  120. default: false
  121. },
  122. disabledIndicatorColor: {
  123. type: String,
  124. default: "#bababa"
  125. },
  126. disabledBlockColor: {
  127. type: String,
  128. default: "#8b8b8b"
  129. },
  130. blockStyle: {
  131. type: Object,
  132. default () {
  133. return {}
  134. }
  135. }
  136. },
  137. mounted() {
  138. console.log('mounted')
  139. const query = uni.createSelectorQuery().in(this);
  140. query.select('.double-slider').boundingClientRect(res => {
  141. this.width = res.width;
  142. const cMinP = this.currentMinValue/ this.maxValue * 100;
  143. const cMaxP = this.currentMaxValue / this.maxValue * 100;
  144. this.activeX = this.width * (cMinP * 0.01);
  145. this.xWidth = this.width * (cMaxP * 0.01);
  146. //this.currentMaxValue = this.currentValue[1];
  147. //this.currentMinValue = this.currentValue[0];
  148. //console.log('value '+this.currentMaxValue)
  149. this.minStepValue = this.currentMinValue;
  150. this.maxStepValue = this.maxValue * (cMaxP * 0.01);
  151. console.log('mounted 1')
  152. }).exec();
  153. query.select('.block1').boundingClientRect(res => {
  154. this.blockWidth = res.width;
  155. console.log('mounted2 ' + this.blockWidth)
  156. }).exec();
  157. },
  158. data() {
  159. return {
  160. width: 0,
  161. xWidth: 0,
  162. activeX: 0,
  163. blockWidth: 0, // 块宽度
  164. blockX1: 0,
  165. blockX2: 0,
  166. overlap: false,
  167. isLeft: false,
  168. minStepValue: 0,
  169. maxStepValue: 0
  170. }
  171. },
  172. computed: {
  173. /* currentMinValue(){
  174. return currentValue[0];
  175. },
  176. currentMaxValue(){
  177. return currentValue[1];
  178. },*/
  179. stepValueW() {
  180. const stepP = this.step / this.maxValue * 100;
  181. return this.width * stepP * 0.01;
  182. },
  183. getBlockStyle() {
  184. this.blockStyle = {
  185. width: this.blockSize,
  186. height: this.blockSize,
  187. ...this.blockStyle,
  188. }
  189. return this.blockStyle;
  190. },
  191. getLableStyle(){
  192. const styleObj = Object.assign({}, this.getBlockStyle);
  193. if(this.borderRadius)
  194. {
  195. styleObj.top = this.borderRadius;
  196. }
  197. if(this.labelColor){
  198. styleObj.color = this.labelColor;
  199. }
  200. return styleObj;
  201. },
  202. getLeftBlockStyle() {
  203. const styleObj = Object.assign({}, this.getBlockStyle);
  204. styleObj.background = styleObj.backgroundImage || styleObj.background ? styleObj.background :
  205. `${this.disabled || this.leftBlockDisabled ? this.disabledBlockColor : this.blockColor}`;
  206. if(this.borderStyle)
  207. styleObj.borderStyle = this.borderStyle;
  208. if(this.blockColor)
  209. styleObj.borderColor = this.borderColor;
  210. if(this.borderWidth)
  211. styleObj.borderWidth = this.borderWidth;
  212. if(this.borderRadius)
  213. styleObj.borderRadius = this.borderRadius;
  214. return styleObj;
  215. },
  216. getRightBlockStyle() {
  217. const styleObj = Object.assign({}, this.getBlockStyle);
  218. styleObj.background = styleObj.backgroundImage || styleObj.background ? styleObj.background :
  219. `${this.disabled || this.rightBlockDisabled ? this.disabledBlockColor : this.blockColor}`;
  220. if(this.borderStyle)
  221. styleObj.borderStyle = this.borderStyle;
  222. if(this.blockColor)
  223. styleObj.borderColor = this.borderColor;
  224. if(this.borderWidth)
  225. styleObj.borderWidth = this.borderWidth;
  226. if(this.borderRadius)
  227. styleObj.borderRadius = this.borderRadius;
  228. return styleObj;
  229. }
  230. },
  231. methods: {
  232. reset(e){
  233. console.log('e'+JSON.stringify(e))
  234. this.activeX = this.width * (e.minP * 0.01);
  235. this.xWidth = this.width * (e.maxP * 0.01);
  236. this.value[0] = e.minValue;
  237. this.value[1] = e.maxValue;
  238. },
  239. // 记录一个块位置
  240. blockStart1(e) {
  241. if (this.disabled || this.leftBlockDisabled) {
  242. return;
  243. }
  244. this.blockX1 = e.touches[0].clientX;
  245. },
  246. // 记录第二个块位置
  247. blockStart2(e) {
  248. this.blockX2 = e.touches[0].clientX;
  249. if (Math.round(this.activeX) == Math.round(this.xWidth)) {
  250. this.overlap = true;
  251. } else {
  252. this.overlap = false;
  253. }
  254. },
  255. // 第一个块移动
  256. blockMove1(e) {
  257. if (this.disabled || this.leftBlockDisabled) {
  258. return;
  259. }
  260. let x = (this.blockX1 + (e.touches[0].clientX - this.blockX1)) - this.blockWidth;
  261. // 如果设置了步长
  262. if (this.step > 0) {
  263. // 移动的数值
  264. let cValue = this.maxValue * (x / this.width * 100 * 0.01);
  265. if (cValue >= this.minStepValue + this.step && Math.round(this.activeX) < Math.round(this.xWidth)) {
  266. this.minStepValue += this.step;
  267. this.activeX += this.stepValueW;
  268. this.sendEmit();
  269. } else if (cValue <= this.minStepValue - this.step && Math.round(this.activeX) > 0) {
  270. this.minStepValue -= this.step;
  271. this.activeX -= this.stepValueW;
  272. this.sendEmit();
  273. }
  274. return;
  275. }
  276. // 如果本次移动的x值超过最大的值或小于最小值
  277. if (x > this.xWidth) {
  278. x = this.xWidth;
  279. } else if (x <= 0) {
  280. x = 0;
  281. }
  282. this.activeX = x;
  283. this.sendEmit();
  284. },
  285. // 第二个块移动
  286. blockMove2(e) {
  287. const activeX = this.activeX;
  288. let x = (this.blockX2 + (e.touches[0].clientX - this.blockX2)) - this.blockWidth;
  289. // 如果是重叠, 并且只对左边生效
  290. if (this.overlap && (x - this.step) <= this.xWidth) {
  291. if (this.disabled || this.leftBlockDisabled) {
  292. return;
  293. }
  294. this.isLeft = true;
  295. let cValue = this.maxValue * (x / this.width * 100 * 0.01);
  296. // 如果有步长
  297. if (this.step > 0) {
  298. if (cValue >= this.minStepValue + this.step && Math.round(this.activeX) < Math.round(this
  299. .xWidth)) {
  300. this.minStepValue += this.step;
  301. this.activeX += this.stepValueW;
  302. this.sendEmit();
  303. } else if (cValue <= this.minStepValue - this.step && Math.round(this.activeX) > 0) {
  304. this.minStepValue -= this.step;
  305. this.activeX -= this.stepValueW;
  306. this.sendEmit();
  307. }
  308. return;
  309. }
  310. // 移动超出设置
  311. if (x <= 0) {
  312. x = 0;
  313. } else if (x >= this.xWidth) { // 如果移动的值大于最大值
  314. x = this.xWidth;
  315. }
  316. this.activeX = x;
  317. this.sendEmit();
  318. } else {
  319. if (this.disabled || this.rightBlockDisabled) {
  320. return;
  321. }
  322. // 重叠之后,第一次拖动是往左边的话将无法超出右边
  323. if (this.isLeft) {
  324. this.activeX = this.xWidth;
  325. this.sendEmit();
  326. return;
  327. }
  328. // 不重叠正常设置
  329. this.overlap = false;
  330. // 如果设置步长
  331. if (this.step > 0) {
  332. // 移动的数值
  333. let cValue = this.maxValue * (x / this.width * 100 * 0.01);
  334. // 判断移动方向
  335. if (cValue >= this.maxStepValue + this.step && Math.round(this.xWidth) < Math.round(this.width)) {
  336. this.maxStepValue += this.step;
  337. this.xWidth += this.stepValueW;
  338. this.sendEmit();
  339. } else if (cValue <= this.maxStepValue - this.step && Math.round(this.xWidth) > Math.round(this
  340. .activeX)) {
  341. this.maxStepValue -= this.step;
  342. this.xWidth -= this.stepValueW;
  343. this.sendEmit();
  344. }
  345. return;
  346. }
  347. // 移动超出设置
  348. if (x <= this.activeX) {
  349. x = this.activeX;
  350. } else if (x >= this.width) { // 如果移动的值大于最大值
  351. x = this.width;
  352. }
  353. // 正常设置
  354. this.xWidth = x;
  355. }
  356. this.sendEmit();
  357. },
  358. calc_position(){
  359. },
  360. sendEmit() {
  361. // 最小百分比
  362. let minP = Math.floor(this.activeX / this.width * 100);
  363. // 最小值
  364. let minValue = Math.floor((this.maxValue * (minP * 0.01)) * 100) / 100;
  365. // 最大百分比
  366. let maxP = Math.floor(this.xWidth / this.width * 100);
  367. // 最大值
  368. let maxValue = Math.floor((this.maxValue * (maxP * 0.01)) * 100) / 100;
  369. this.currentMinValue = minValue;
  370. this.currentMaxValue = maxValue;
  371. //console.log('min value'+this.currentMinValue)
  372. //console.log('max value'+this.currentMaxValue)
  373. // console.log("最小百分比",minP);
  374. // console.log("最小值",minValue);
  375. // console.log("最大百分比",maxP);
  376. // console.log("最大值",maxValue);
  377. this.$emit("change", {
  378. minValue,
  379. maxValue,
  380. minP,
  381. maxP
  382. })
  383. }
  384. }
  385. }
  386. </script>
  387. <style scoped lang="scss">
  388. .double-slider {
  389. margin: 0 auto;
  390. .active-slider {
  391. position: relative;
  392. .block1,
  393. .block2 {
  394. border-radius: 50%;
  395. position: absolute;
  396. top: 50%;
  397. transition: all 0.05s;
  398. }
  399. .block1 {
  400. left: 0;
  401. transform: translate(-50%, -50%);
  402. }
  403. .block2 {
  404. right: 0;
  405. transform: translate(50%, -50%);
  406. z-index: 1;
  407. }
  408. }
  409. }
  410. </style>