瀏覽代碼

Slider: add vertical mode

devange 8 年之前
父節點
當前提交
8e1d8329aa

+ 32 - 1
examples/docs/en-US/slider.md

@@ -10,7 +10,8 @@
         value6: 0,
         value7: 0,
         value8: 0,
-        value9: [4, 8]
+        value9: [4, 8],
+        value10: 0
       };
     },
     methods: {
@@ -171,6 +172,34 @@ Selecting a range of values is supported.
 ```
 :::
 
+### Vertical mode
+
+Vertical slider
+
+:::demo Setting the `vertical` attribute to switch to the vertical mode, the `size` attribute must be setted as the heigth of slider
+```html
+<template>
+  <div class="block">
+    <el-slider
+      v-model="value10"
+      vertical
+      size="300px">
+    </el-slider>
+  </div>
+</template>
+
+<script>
+  export default {
+    data() {
+      return {
+        value10: 0
+      }
+    }
+  }
+</script>
+```
+:::
+
 ## Attributes
 | Attribute      | Description          | Type      | Accepted Values       | Default  |
 |---------- |-------------- |---------- |--------------------------------  |-------- |
@@ -184,6 +213,8 @@ Selecting a range of values is supported.
 | show-tooltip | whether to display tooltip value | boolean | — | true |
 | format-tooltip | format to display tooltip value | Function(value) | — | — |
 | range | whether to select a range | boolean | — | false |
+| vertical | vertical mode | boolean | — | false |
+| size | width or height, it should be setted when vertical mode | String | — | - |
 
 ## Events
 | Event Name | Description | Parameters |

+ 32 - 1
examples/docs/zh-CN/slider.md

@@ -10,7 +10,8 @@
         value6: 0,
         value7: 0,
         value8: 0,
-        value9: [4, 8]
+        value9: [4, 8],
+        value10: 0
       };
     },
     methods: {
@@ -195,6 +196,34 @@
 ```
 :::
 
+### 竖向模式
+
+竖向滑块
+
+:::demo 设置`vertical`可使滑块变成竖向模式,必须设置滑块高度`size`属性
+```html
+<template>
+  <div class="block">
+    <el-slider
+      v-model="value10"
+      vertical
+      size="300px">
+    </el-slider>
+  </div>
+</template>
+
+<script>
+  export default {
+    data() {
+      return {
+        value10: 0
+      }
+    }
+  }
+</script>
+```
+:::
+
 ### Attributes
 | 参数      | 说明          | 类型      | 可选值                           | 默认值  |
 |---------- |-------------- |---------- |--------------------------------  |-------- |
@@ -208,6 +237,8 @@
 | show-tooltip | 是否显示 tooltip | boolean | — | true |
 | format-tooltip | 格式化 tooltip message | Function(value) | — | — |
 | range | 是否为范围选择 | boolean | — | false |
+| vertical | 是否竖向模式 | boolean | — | false |
+| size | 宽度或高度,竖向模式时务必设置 | String | — | - |
 
 ### Events
 | 事件名称      | 说明    | 回调参数      |

+ 20 - 4
packages/slider/src/button.vue

@@ -5,7 +5,7 @@
     @mouseleave="handleMouseLeave"
     @mousedown="onButtonDown"
     :class="{ 'hover': hovering, 'dragging': dragging }"
-    :style="{ left: currentPosition }"
+    :style="this.vertical ? { bottom: currentPosition } : { left: currentPosition }"
     ref="button">
     <el-tooltip placement="top" ref="tooltip" :disabled="!showTooltip">
       <span slot="content">{{ formatValue }}</span>
@@ -28,6 +28,10 @@
       value: {
         type: Number,
         default: 0
+      },
+      vertical: {
+        type: Boolean,
+        default: false
       }
     },
 
@@ -37,6 +41,8 @@
         dragging: false,
         startX: 0,
         currentX: 0,
+        startY: 0,
+        currentY: 0,
         startPosition: 0,
         newPosition: null,
         oldValue: this.value
@@ -117,15 +123,25 @@
 
       onDragStart(event) {
         this.dragging = true;
-        this.startX = event.clientX;
+        if (this.vertical) {
+          this.startY = event.clientY;
+        } else {
+          this.startX = event.clientX;
+        }
         this.startPosition = parseFloat(this.currentPosition);
       },
 
       onDragging(event) {
         if (this.dragging) {
           this.displayTooltip();
-          this.currentX = event.clientX;
-          const diff = (this.currentX - this.startX) / this.$parent.$sliderWidth * 100;
+          let diff = 0;
+          if (this.vertical) {
+            this.currentY = event.clientY;
+            diff = (this.startY - this.currentY) / this.$parent.$sliderSize * 100;
+          } else {
+            this.currentX = event.clientX;
+            diff = (this.currentX - this.startX) / this.$parent.$sliderSize * 100;
+          }
           this.newPosition = this.startPosition + diff;
           this.setPosition(this.newPosition);
         }

+ 30 - 11
packages/slider/src/main.vue

@@ -1,5 +1,6 @@
 <template>
-  <div class="el-slider">
+  <div class="el-slider"
+    :class="{ 'el-slider__vertical': vertical, 'el-slider__with_input': showInput }">
     <el-input-number
       v-model="firstValue"
       v-if="showInput && !range"
@@ -14,20 +15,26 @@
     </el-input-number>
     <div class="el-slider__runway"
       :class="{ 'show-input': showInput, 'disabled': disabled }"
+      :style="vertical ? size && { height: size } : size && { width: size }"
       @click="onSliderClick"
       ref="slider">
       <div
         class="el-slider__bar"
-        :style="{
-          width: barWidth,
-          left: barLeft
+        :style="vertical ? {
+          height: barSize,
+          bottom: barStart
+        } : {
+          width: barSize,
+          left: barStart
         }">
       </div>
       <slider-button
+        :vertical="vertical"
         v-model="firstValue"
         ref="button1">
       </slider-button>
       <slider-button
+        :vertical="vertical"
         v-model="secondValue"
         ref="button2"
         v-if="range">
@@ -35,7 +42,7 @@
       <div
         class="el-slider__stop"
         v-for="item in stops"
-        :style="{ 'left': item + '%' }"
+        :style="vertical ? { 'bottom': item + '%' } : { 'left': item + '%' }"
         v-if="showStops">
       </div>
     </div>
@@ -94,6 +101,13 @@
       range: {
         type: Boolean,
         default: false
+      },
+      vertical: {
+        type: Boolean,
+        default: false
+      },
+      size: {
+        type: String
       }
     },
 
@@ -213,14 +227,19 @@
 
       onSliderClick(event) {
         if (this.disabled || this.dragging) return;
-        const sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left;
-        this.setPosition((event.clientX - sliderOffsetLeft) / this.$sliderWidth * 100);
+        if (this.vertical) {
+          const sliderOffsetBottom = this.$refs.slider.getBoundingClientRect().bottom;
+          this.setPosition((event.clientY - sliderOffsetBottom) / this.$sliderSize * 100);
+        } else {
+          const sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left;
+          this.setPosition((event.clientX - sliderOffsetLeft) / this.$sliderSize * 100);
+        }
       }
     },
 
     computed: {
-      $sliderWidth() {
-        return parseInt(getStyle(this.$refs.slider, 'width'), 10);
+      $sliderSize() {
+        return parseInt(getStyle(this.$refs.slider, (this.vertical ? 'height' : 'width')), 10);
       },
 
       stops() {
@@ -248,13 +267,13 @@
         return Math.max(this.firstValue, this.secondValue);
       },
 
-      barWidth() {
+      barSize() {
         return this.range
           ? `${ 100 * (this.maxValue - this.minValue) / (this.max - this.min) }%`
           : `${ 100 * (this.firstValue - this.min) / (this.max - this.min) }%`;
       },
 
-      barLeft() {
+      barStart() {
         return this.range
           ? `${ 100 * (this.minValue - this.min) / (this.max - this.min) }%`
           : '0%';

+ 75 - 0
packages/theme-default/src/slider.css

@@ -128,5 +128,80 @@
       background-color: var(--slider-stop-background-color);
       transform: translateX(-50%);
     }
+
+    @e vertical {
+      position: relative;
+      .el-slider__runway {
+        width: 4px;
+        height: 100%;
+        margin: 0 16px;
+      }
+      .el-slider__bar {
+        width: 4px;
+        height: auto;
+        border-radius: 0 0 3px 3px;
+      }
+      .el-slider__button-wrapper {
+        top: auto;
+        left: var(--slider-button-wrapper-offset);
+        transform: translateY(50%);
+      }
+      .el-slider__stop {
+        transform: translateY(50%);
+      }
+      &.el-slider__with_input {
+        padding-bottom: var(--input-large-height);
+        .el-slider__input {
+          overflow: visible;
+          float: none;
+          position: absolute;
+          bottom: 0;
+          width: 36px;
+          margin-top: 15px;
+          .el-input__inner {
+            text-align: center;
+            padding-left: 5px;
+            padding-right: 5px;
+          }
+          .el-input-number__decrease,
+          .el-input-number__increase
+          {
+            top: var(--input-small-height);
+            margin-top: -1px;
+            border: var(--input-border);
+            line-height: 20px;
+            box-sizing: border-box;
+            transition: var(--border-transition-base);
+          }
+          .el-input-number__decrease {
+            width: 18px;
+            right: 18px;
+            border-bottom-left-radius: var(--input-border-radius);
+          }
+          .el-input-number__increase {
+            width: 19px;
+            border-bottom-right-radius: var(--input-border-radius);
+            & ~ .el-input .el-input__inner {
+              border-bottom-left-radius: 0;
+              border-bottom-right-radius: 0;
+            }
+          }
+          &:hover {
+            .el-input-number__decrease,
+            .el-input-number__increase
+            {
+              border-color: var(--input-hover-border);
+            }
+          }
+          &:active {
+            .el-input-number__decrease,
+            .el-input-number__increase
+            {
+              border-color: var(--input-focus-border);
+            }
+          }
+        }
+      }
+    }
   }
 }