vuepress/vue动态更换配色方案

vuepress/vue动态更换配色方案

最后修改时间:7 months ago

TODO 上一直挂着调色盘,原想可以去网上白嫖一个插件该是分分钟的事儿,没想怎么也没找到心仪的,只好自己动手改造了一个。 思路:

  1. 延续 vuepresscss 预处理器,为样式配置好各种颜色变量,接着在使用该颜色时,使用变量进行替代,以实现便捷地颜色大面积更换。 但很可惜的是, stylus 本身是无法使用脚本对其进行操作的。所以,我们只能折衷采用 css 的变量作为桥梁。 以 vuepress 默认博客主题为例,将入口文件 docs\.vuepress\theme\styles\index.styl 进行简单处理。
body
  --main-color $accentColor
1
2
  1. 检索主题下的所有文件,将所有采用 $accentColor 处(除配色版本身的变量配置与上述入口文件处),用 var(--main-color) 替代,因编译器限制,这里无法使用stylus的内置方法 darkenlighten,经测试,也无法采用原生的 color 函数[1] ,只能妥协采用相同配色。

  2. 接着,参考 vcolorpicker[2] 提取 ColorPicker.vue 源码:

<template lang="html">
  <div class="m-colorPicker"  ref="colorPicker" v-clickoutside="closePanel" v-on:click="event => { event.stopPropagation() }">
    <!-- 颜色显示小方块 -->
    <div class="colorBtn"
      v-bind:style="`background-color: ${showColor}`"
      v-on:click="openPanel"
      v-bind:class="{ disabled: disabled }"
    ></div>
    <!-- 颜色色盘 -->
    <div class="box" v-bind:class="{ open: openStatus }" >
      <div class="hd">
        <div class="colorView" v-bind:style="`background-color: ${showPanelColor}`"></div>
        <div class="defaultColor"
          v-on:click="handleDefaultColor"
          v-on:mouseover="hoveColor = defaultColor"
          v-on:mouseout="hoveColor = null"
        >默认颜色</div>
      </div>
      <div class="bd">
        <h3>主题颜色</h3>
        <ul class="tColor">
          <li
            v-for="(color, index) of tColor"
            :key="index"
            v-bind:style="{ backgroundColor: color }"
            v-on:mouseover="hoveColor = color"
            v-on:mouseout="hoveColor = null"
            v-on:click="updataValue(color)"
          ></li>
        </ul>
        <ul class="bColor">
          <li v-for="(item, index) of colorPanel" :key="index">
            <ul>
              <li
                v-for="(color, cindex) of item"
                :key="cindex"
                v-bind:style="{ backgroundColor: color }"
                v-on:mouseover="hoveColor = color"
                v-on:mouseout="hoveColor = null"
                v-on:click="updataValue(color)"
              ></li>
            </ul>
          </li>
        </ul>
        <h3>标准颜色</h3>
        <ul class="tColor">
          <li
            v-for="(color, index) of bColor"
            :key="index"
            v-bind:style="{ backgroundColor: color }"
            v-on:mouseover="hoveColor = color"
            v-on:mouseout="hoveColor = null"
            v-on:click="updataValue(color)"
          ></li>
        </ul>
        <h3 v-on:click="triggerHtml5Color">更多颜色...</h3>
        <!-- 用以激活HTML5颜色面板 -->
        <input type="color"
          ref="html5Color"
          v-model="html5Color"
          v-on:change="updataValue(html5Color)">
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "colorPicker",
  directives: {
    clickoutside,
  },
  props: {
    // 当前颜色值
    value: {
      type: String,
      required: true,
    },
    // 默认颜色
    defaultColor: {
      type: String,
      default: "#000000",
    },
    // 禁用状态
    disabled: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      // 面板打开状态
      openStatus: false,
      // 鼠标经过的颜色块
      hoveColor: null,
      // 主题颜色
      tColor: [
        "#000000",
        "#ffffff",
        "#eeece1",
        "#1e497b",
        "#4e81bb",
        "#e2534d",
        "#9aba60",
        "#8165a0",
        "#47acc5",
        "#f9974c",
      ],
      // 颜色面板
      colorConfig: [
        ["#7f7f7f", "#f2f2f2"],
        ["#0d0d0d", "#808080"],
        ["#1c1a10", "#ddd8c3"],
        ["#0e243d", "#c6d9f0"],
        ["#233f5e", "#dae5f0"],
        ["#632623", "#f2dbdb"],
        ["#4d602c", "#eaf1de"],
        ["#3f3150", "#e6e0ec"],
        ["#1e5867", "#d9eef3"],
        ["#99490f", "#fee9da"],
      ],
      // 标准颜色
      bColor: [
        "#c21401",
        "#ff1e02",
        "#ffc12a",
        "#ffff3a",
        "#90cf5b",
        "#00af57",
        "#00afee",
        "#0071be",
        "#00215f",
        "#72349d",
      ],
      html5Color: this.value,
    };
  },
  computed: {
    // 显示面板颜色
    showPanelColor() {
      if (this.hoveColor) {
        return this.hoveColor;
      } else {
        return this.showColor;
      }
    },
    // 显示颜色
    showColor() {
      if (this.value) {
        return this.value;
      } else {
        return this.defaultColor;
      }
    },
    // 颜色面板
    colorPanel() {
      let colorArr = [];
      for (let color of this.colorConfig) {
        colorArr.push(this.gradient(color[1], color[0], 5));
      }
      return colorArr;
    },
  },
  methods: {
    openPanel() {
      this.openStatus = !this.disabled;
    },
    closePanel() {
      this.openStatus = false;
    },
    triggerHtml5Color() {
      this.$refs.html5Color.click();
    },
    // 更新组件的值 value
    updataValue(value) {
      this.$emit("input", value);
      this.$emit("change", value);
      this.openStatus = false;
    },
    // 设置默认颜色
    handleDefaultColor() {
      this.updataValue(this.defaultColor);
    },
    // 格式化 hex 颜色值
    parseColor(hexStr) {
      if (hexStr.length === 4) {
        hexStr =
          "#" +
          hexStr[1] +
          hexStr[1] +
          hexStr[2] +
          hexStr[2] +
          hexStr[3] +
          hexStr[3];
      } else {
        return hexStr;
      }
    },
    // RGB 颜色 转 HEX 颜色
    rgbToHex(r, g, b) {
      let hex = ((r << 16) | (g << 8) | b).toString(16);
      return "#" + new Array(Math.abs(hex.length - 7)).join("0") + hex;
    },
    // HEX 转 RGB 颜色
    hexToRgb(hex) {
      hex = this.parseColor(hex);
      let rgb = [];
      for (let i = 1; i < 7; i += 2) {
        rgb.push(parseInt("0x" + hex.slice(i, i + 2)));
      }
      return rgb;
    },
    // 计算渐变过渡颜色
    gradient(startColor, endColor, step) {
      // 讲 hex 转换为 rgb
      let sColor = this.hexToRgb(startColor);
      let eColor = this.hexToRgb(endColor);
      // 计算R\G\B每一步的差值
      let rStep = (eColor[0] - sColor[0]) / step;
      let gStep = (eColor[1] - sColor[1]) / step;
      let bStep = (eColor[2] - sColor[2]) / step;
      let gradientColorArr = [];
      // 计算每一步的hex值
      for (let i = 0; i < step; i++) {
        gradientColorArr.push(
          this.rgbToHex(
            parseInt(rStep * i + sColor[0]),
            parseInt(gStep * i + sColor[1]),
            parseInt(bStep * i + sColor[2])
          )
        );
      }
      return gradientColorArr;
    },
  },
};
</script>

<style lang="css" scoped>
.m-colorPicker {
  font-size: 14px;
  display: inline-block;
  outline: none;
}
.m-colorPicker ul,
li,
ol {
  list-style: none;
  margin: 0;
  padding: 0;
}
.m-colorPicker .colorBtn {
  width: 20px;
  height: 20px;
  font-size: 12px;
  line-height: 20px;
  color:#fff;
}
.m-colorPicker .colorBtn.disabled {
  cursor: no-drop;
}
.m-colorPicker .box {
  position: absolute;
  width: 190px;
  left:-195px;
  background: #fff;
  border: 1px solid #ddd;
  visibility: hidden;
  border-radius: 2px;
  margin-top: 2px;
  padding: 10px;
  padding-bottom: 5px;
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.15);
  opacity: 0;
  transition: all 0.3s ease;
  box-sizing: content-box;
}
.m-colorPicker .box h3 {
  margin: 0;
  font-size: 14px;
  font-weight: normal;
  margin-top: 10px;
  margin-bottom: 5px;
  line-height: 1;
  color: #333;
}
.m-colorPicker .box input {
  visibility: hidden;
  position: absolute;
  left: 0;
  bottom: 0;
}

.m-colorPicker .box.open {
  visibility: visible;
  opacity: 1;
  z-index: 1;
}
.m-colorPicker .hd {
  overflow: hidden;
  line-height: 29px;
}
.m-colorPicker .hd .colorView {
  width: 100px;
  height: 30px;
  float: left;
  transition: background-color 0.3s ease;
}
.m-colorPicker .hd .defaultColor {
  width: 80px;
  float: right;
  text-align: center;
  border: 1px solid #ddd;
  cursor: pointer;
  color: #333;
}

.m-colorPicker .tColor li {
  width: 15px;
  height: 15px;
  display: inline-block;
  margin: 0 2px;
  transition: all 0.3s ease;
}
.m-colorPicker .tColor li:hover {
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.4);
  transform: scale(1.3);
}

.m-colorPicker .bColor li {
  width: 15px;
  display: inline-block;
  margin: 0 2px;
}
.m-colorPicker .bColor li li {
  display: block;
  width: 15px;
  height: 15px;
  transition: all 0.3s ease;
  margin: 0;
}
.m-colorPicker .bColor li li:hover {
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.4);
  transform: scale(1.3);
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346

clickoutside.js[3]

function validate(binding) {
  if (typeof binding.value !== 'function') {
    console.warn('[Vue-click-outside:] provided expression', binding.expression, 'is not a function.')
    return false
  }

  return true
}

function isPopup(popupItem, elements) {
  if (!popupItem || !elements)
    return false

  for (var i = 0, len = elements.length; i < len; i++) {
    try {
      if (popupItem.contains(elements[i])) {
        return true
      }
      if (elements[i].contains(popupItem)) {
        return false
      }
    } catch(e) {
      return false
    }
  }

  return false
}

function isServer(vNode) {
  return typeof vNode.componentInstance !== 'undefined' && vNode.componentInstance.$isServer
}

exports = module.exports = {
  bind: function (el, binding, vNode) {
    if (!validate(binding)) return

    // Define Handler and cache it on the element
    function handler(e) {
      if (!vNode.context) return

      // some components may have related popup item, on which we shall prevent the click outside event handler.
      var elements = e.path || (e.composedPath && e.composedPath())
      elements && elements.length > 0 && elements.unshift(e.target)

      if (el.contains(e.target) || isPopup(vNode.context.popupItem, elements)) return

      el.__vueClickOutside__.callback(e)
    }

    // add Event Listeners
    el.__vueClickOutside__ = {
      handler: handler,
      callback: binding.value
    }
    const clickHandler = 'ontouchstart' in document.documentElement ? 'touchstart' : 'click';
    !isServer(vNode) && document.addEventListener(clickHandler, handler)
  },

  update: function (el, binding) {
    if (validate(binding)) el.__vueClickOutside__.callback = binding.value
  },

  unbind: function (el, binding, vNode) {
    // Remove Event Listeners
    const clickHandler = 'ontouchstart' in document.documentElement ? 'touchstart' : 'click';
    !isServer(vNode) && el.__vueClickOutside__ && document.removeEventListener(clickHandler, el.__vueClickOutside__.handler)
    delete el.__vueClickOutside__
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
  1. 在想要使用的地方引入组件
<ColorPicker
    v-model="color" // 绑定的属性
    defaultColor="#ef7c1e" // 默认颜色
    v-on:change="headleChangeColor" // 绑定的 change 事件
/>
1
2
3
4
5

接着处理 headleChangeColor 事件

  mounted() {
    const themeColor = localStorage.getItem("themeColor") || "#ef7c1e";
    if (themeColor) this.headleChangeColor(themeColor);
    this.color = themeColor;
  },
  methods: {
    headleChangeColor(e) {
      const style = document.body.style;
      style.setProperty("--main-color", e);
      localStorage.setItem("themeColor", e);
    },
  },    
1
2
3
4
5
6
7
8
9
10
11
12
  1. 对插件的样式进行适配调整 例,在 index.stylus 最下新增,覆盖默认样式:
.go-to-top
  color var(--main-color)!important
.pagination
  .active > a, > .active > span, > .active > a:hover, > .active > span:hover,> .active > a:focus, > .active > span:focus
    background-color: var(--main-color)!important
    border-color: var(--main-color)!important
    color: #fff!important
  > li > a, .pagination > li > span
    color:  var(--main-color)!important
  > .disabled > span, .disabled > span:hover, .disabled > span:focus, .disabled > a,  .disabled > a:hover, .disabled > a:focus
    color: #ddd!important
#nprogress .bar
  background: var(--main-color)!important;
1
2
3
4
5
6
7
8
9
10
11
12
13

最后去查看下效果吧。

当然,因为这边的需求较为简单,请依据个人需求自行再调试。

另外,经测试IE11下无效


  1. 例:color(#efb4ea alpha(58%) hue(360) saturation(78%) shade(18%)) 参考 (opens new window) ↩︎

  2. vue-color-picker (opens new window) ↩︎

  3. click-outside (opens new window) ↩︎

- 全文完 -

留下一条留言?
默认颜色

主题颜色

标准颜色

更多颜色...