update ColorPicker
This commit is contained in:
parent
dab3947668
commit
e7893a68ed
12 changed files with 994 additions and 26 deletions
|
@ -1 +1,2 @@
|
|||
src/directives
|
||||
src/utils/throttle.js
|
22
LICENSE
22
LICENSE
|
@ -152,3 +152,25 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 greyby
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
<template>
|
||||
<div style="margin: 100px;">
|
||||
{{ color.rgba }}
|
||||
<!--<Input placeholder="请输入..." size="large" style="width: 50px;"></Input>-->
|
||||
<!--<color-picker placement="bottom-start" size="large"></color-picker>-->
|
||||
<!--<Date-picker type="date" placeholder="选择日期" size="large" style="width: 200px"></Date-picker>-->
|
||||
<color-picker recommend alpha placement="bottom" size="default"></color-picker>
|
||||
<color-picker v-model="color" alpha :recommend="true" placement="bottom" size="default"></color-picker>
|
||||
<color-picker v-model="color" :alpha="false" :recommend="false" placement="bottom" size="default"></color-picker>
|
||||
<!--<Date-picker type="date" placeholder="选择日期" style="width: 200px"></Date-picker>-->
|
||||
<!--<color-picker placement="bottom-start" size="small"></color-picker>-->
|
||||
<!--<Date-picker type="date" placeholder="选择日期" size="small" style="width: 200px"></Date-picker>-->
|
||||
|
@ -13,7 +15,30 @@
|
|||
export default {
|
||||
props: {},
|
||||
data () {
|
||||
return {};
|
||||
return {
|
||||
color: {
|
||||
hex: '#194d33',
|
||||
hsl: {
|
||||
h: 150,
|
||||
s: 0.5,
|
||||
l: 0.2,
|
||||
a: 1
|
||||
},
|
||||
hsv: {
|
||||
h: 150,
|
||||
s: 0.66,
|
||||
v: 0.30,
|
||||
a: 1
|
||||
},
|
||||
rgba: {
|
||||
r: 25,
|
||||
g: 77,
|
||||
b: 51,
|
||||
a: 1
|
||||
},
|
||||
a: 1
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {},
|
||||
methods: {}
|
||||
|
|
7
package-lock.json
generated
7
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "iview",
|
||||
"version": "2.0.0-rc.18",
|
||||
"version": "2.1.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -15043,6 +15043,11 @@
|
|||
"setimmediate": "1.0.5"
|
||||
}
|
||||
},
|
||||
"tinycolor2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
|
||||
"integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
|
||||
},
|
||||
"tmp": {
|
||||
"version": "0.0.31",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz",
|
||||
|
|
|
@ -43,7 +43,8 @@
|
|||
"async-validator": "^1.7.1",
|
||||
"core-js": "^2.4.1",
|
||||
"deepmerge": "^1.5.0",
|
||||
"popper.js": "^0.6.4"
|
||||
"popper.js": "^0.6.4",
|
||||
"tinycolor2": "^1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^2.4.1"
|
||||
|
|
77
src/components/color-picker/alpha.vue
Normal file
77
src/components/color-picker/alpha.vue
Normal file
|
@ -0,0 +1,77 @@
|
|||
<template>
|
||||
<div class="ivu-color-picker-alpha">
|
||||
<div class="ivu-color-picker-alpha-checkboard-wrap">
|
||||
<div class="ivu-color-picker-alpha-checkerboard"></div>
|
||||
</div>
|
||||
<div class="ivu-color-picker-alpha-gradient" :style="{background: gradientColor}"></div>
|
||||
<div class="ivu-color-picker-alpha-container" ref="container"
|
||||
@mousedown="handleMouseDown"
|
||||
@touchmove="handleChange"
|
||||
@touchstart="handleChange">
|
||||
<div class="ivu-color-picker-alpha-pointer" :style="{left: colors.a * 100 + '%'}">
|
||||
<div class="ivu-color-picker-alpha-picker"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'Alpha',
|
||||
props: {
|
||||
value: Object,
|
||||
onChange: Function
|
||||
},
|
||||
computed: {
|
||||
colors () {
|
||||
return this.value;
|
||||
},
|
||||
gradientColor () {
|
||||
const rgba = this.colors.rgba;
|
||||
const rgbStr = [rgba.r, rgba.g, rgba.b].join(',');
|
||||
return 'linear-gradient(to right, rgba(' + rgbStr + ', 0) 0%, rgba(' + rgbStr + ', 1) 100%)';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChange (e, skip) {
|
||||
!skip && e.preventDefault();
|
||||
const container = this.$refs.container;
|
||||
const containerWidth = container.clientWidth;
|
||||
|
||||
const xOffset = container.getBoundingClientRect().left + window.pageXOffset;
|
||||
const pageX = e.pageX || (e.touches ? e.touches[0].pageX : 0);
|
||||
const left = pageX - xOffset;
|
||||
|
||||
let a;
|
||||
if (left < 0) {
|
||||
a = 0;
|
||||
} else if (left > containerWidth) {
|
||||
a = 1;
|
||||
} else {
|
||||
a = Math.round(left * 100 / containerWidth) / 100;
|
||||
}
|
||||
|
||||
if (this.colors.a !== a) {
|
||||
this.$emit('change', {
|
||||
h: this.colors.hsl.h,
|
||||
s: this.colors.hsl.s,
|
||||
l: this.colors.hsl.l,
|
||||
a: a,
|
||||
source: 'rgba'
|
||||
});
|
||||
}
|
||||
},
|
||||
handleMouseDown (e) {
|
||||
this.handleChange(e, true);
|
||||
window.addEventListener('mousemove', this.handleChange);
|
||||
window.addEventListener('mouseup', this.handleMouseUp);
|
||||
},
|
||||
handleMouseUp () {
|
||||
this.unbindEventListeners();
|
||||
},
|
||||
unbindEventListeners () {
|
||||
window.removeEventListener('mousemove', this.handleChange);
|
||||
window.removeEventListener('mouseup', this.handleMouseUp);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -8,12 +8,14 @@
|
|||
</div>
|
||||
<Dropdown-menu slot="list">
|
||||
<div :class="[prefixCls + '-picker']">
|
||||
<div :class="[prefixCls + '-picker-panel']"></div>
|
||||
<div :class="[prefixCls + '-picker-panel']">
|
||||
<Saturation v-model="saturationColors" @change="childChange"></Saturation>
|
||||
</div>
|
||||
<div :class="[prefixCls + '-picker-hue-slider']">
|
||||
<Slider v-model="hueNumber" :min="0" :max="255"></Slider>
|
||||
<Hue v-model="saturationColors" @change="childChange"></Hue>
|
||||
</div>
|
||||
<div v-if="alpha" :class="[prefixCls + '-picker-alpha-slider']">
|
||||
<Slider v-model="alphaNumber" :min="0" :max="100"></Slider>
|
||||
<Alpha v-model="saturationColors" @change="childChange"></Alpha>
|
||||
</div>
|
||||
<recommend-colors v-if="colors.length" :list="colors" :class="[prefixCls + '-picker-colors']"></recommend-colors>
|
||||
<recommend-colors v-if="!colors.length && recommend" :list="recommendedColor" :class="[prefixCls + '-picker-colors']"></recommend-colors>
|
||||
|
@ -23,22 +25,76 @@
|
|||
</Dropdown>
|
||||
</template>
|
||||
<script>
|
||||
import tinycolor from 'tinycolor2';
|
||||
|
||||
import Dropdown from '../dropdown/dropdown.vue';
|
||||
import DropdownMenu from '../dropdown/dropdown-menu.vue';
|
||||
import Slider from '../slider/slider.vue';
|
||||
import RecommendColors from './recommend-colors.vue';
|
||||
import Confirm from '../date-picker/base/confirm.vue';
|
||||
|
||||
import Saturation from './saturation.vue';
|
||||
import Hue from './hue.vue';
|
||||
import Alpha from './alpha.vue';
|
||||
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-color-picker';
|
||||
const inputPrefixCls = 'ivu-input';
|
||||
|
||||
function _colorChange (data, oldHue) {
|
||||
const alpha = data && data.a;
|
||||
let color;
|
||||
|
||||
// hsl is better than hex between conversions
|
||||
if (data && data.hsl) {
|
||||
color = tinycolor(data.hsl);
|
||||
} else if (data && data.hex && data.hex.length > 0) {
|
||||
color = tinycolor(data.hex);
|
||||
} else {
|
||||
color = tinycolor(data);
|
||||
}
|
||||
|
||||
if (color && (color._a === undefined || color._a === null)) {
|
||||
color.setAlpha(alpha || 1);
|
||||
}
|
||||
|
||||
const hsl = color.toHsl();
|
||||
const hsv = color.toHsv();
|
||||
|
||||
if (hsl.s === 0) {
|
||||
hsv.h = hsl.h = data.h || (data.hsl && data.hsl.h) || oldHue || 0;
|
||||
}
|
||||
|
||||
// when the hsv.v is less than 0.0164 (base on test)
|
||||
// because of possible loss of precision
|
||||
// the result of hue and saturation would be miscalculated
|
||||
if (hsv.v < 0.0164) {
|
||||
hsv.h = data.h || (data.hsv && data.hsv.h) || 0;
|
||||
hsv.s = data.s || (data.hsv && data.hsv.s) || 0;
|
||||
}
|
||||
|
||||
if (hsl.l < 0.01) {
|
||||
hsl.h = data.h || (data.hsl && data.hsl.h) || 0;
|
||||
hsl.s = data.s || (data.hsl && data.hsl.s) || 0;
|
||||
}
|
||||
|
||||
return {
|
||||
hsl: hsl,
|
||||
hex: color.toHexString().toUpperCase(),
|
||||
rgba: color.toRgb(),
|
||||
hsv: hsv,
|
||||
oldHue: data.h || oldHue || hsl.h,
|
||||
source: data.source,
|
||||
a: data.a || color.getAlpha()
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'ColorPicker',
|
||||
components: { Dropdown, DropdownMenu, Slider, Confirm, RecommendColors },
|
||||
components: { Dropdown, DropdownMenu, Confirm, RecommendColors, Saturation, Hue, Alpha },
|
||||
props: {
|
||||
value: {
|
||||
type: String
|
||||
type: Object
|
||||
},
|
||||
alpha: {
|
||||
type: Boolean,
|
||||
|
@ -81,10 +137,8 @@
|
|||
},
|
||||
data () {
|
||||
return {
|
||||
val: _colorChange(this.value),
|
||||
prefixCls: prefixCls,
|
||||
currentValue: this.value,
|
||||
hueNumber: 0,
|
||||
alphaNumber: 0,
|
||||
recommendedColor: [
|
||||
'#2d8cf0',
|
||||
'#19be6b',
|
||||
|
@ -110,6 +164,15 @@
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
saturationColors: {
|
||||
get () {
|
||||
return this.val;
|
||||
},
|
||||
set (newVal) {
|
||||
this.val = newVal;
|
||||
this.$emit('input', newVal);
|
||||
}
|
||||
},
|
||||
wrapClasses () {
|
||||
return [
|
||||
`${prefixCls}-rel`,
|
||||
|
@ -129,8 +192,41 @@
|
|||
];
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (newVal) {
|
||||
this.val = _colorChange(newVal);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
childChange (data) {
|
||||
this.colorChange(data);
|
||||
},
|
||||
colorChange (data, oldHue) {
|
||||
this.oldHue = this.saturationColors.hsl.h;
|
||||
this.saturationColors = _colorChange(data, oldHue || this.oldHue);
|
||||
},
|
||||
isValidHex (hex) {
|
||||
return tinycolor(hex).isValid();
|
||||
},
|
||||
simpleCheckForValidColor (data) {
|
||||
const keysToCheck = ['r', 'g', 'b', 'a', 'h', 's', 'l', 'v'];
|
||||
let checked = 0;
|
||||
let passed = 0;
|
||||
|
||||
for (let i = 0; i < keysToCheck.length; i++) {
|
||||
const letter = keysToCheck[i];
|
||||
if (data[letter]) {
|
||||
checked++;
|
||||
if (!isNaN(data[letter])) {
|
||||
passed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (checked === passed) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
86
src/components/color-picker/hue.vue
Normal file
86
src/components/color-picker/hue.vue
Normal file
|
@ -0,0 +1,86 @@
|
|||
<template>
|
||||
<div class="ivu-color-picker-hue">
|
||||
<div class="ivu-color-picker-hue-container" ref="container"
|
||||
@mousedown="handleMouseDown"
|
||||
@touchmove="handleChange"
|
||||
@touchstart="handleChange">
|
||||
<div class="ivu-color-picker-hue-pointer" :style="{top: 0, left: pointerLeft}">
|
||||
<div class="ivu-color-picker-hue-picker"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'Hue',
|
||||
props: {
|
||||
value: Object
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
oldHue: 0,
|
||||
pullDirection: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
colors () {
|
||||
const h = this.value.hsl.h;
|
||||
if (h !== 0 && h - this.oldHue > 0) this.pullDirection = 'right';
|
||||
if (h !== 0 && h - this.oldHue < 0) this.pullDirection = 'left';
|
||||
this.oldHue = h;
|
||||
|
||||
return this.value;
|
||||
},
|
||||
pointerLeft () {
|
||||
if (this.colors.hsl.h === 0 && this.pullDirection === 'right') return '100%';
|
||||
return (this.colors.hsl.h * 100) / 360 + '%';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChange (e, skip) {
|
||||
!skip && e.preventDefault();
|
||||
|
||||
const container = this.$refs.container;
|
||||
const containerWidth = container.clientWidth;
|
||||
|
||||
const xOffset = container.getBoundingClientRect().left + window.pageXOffset;
|
||||
const pageX = e.pageX || (e.touches ? e.touches[0].pageX : 0);
|
||||
const left = pageX - xOffset;
|
||||
|
||||
let h;
|
||||
let percent;
|
||||
|
||||
if (left < 0) {
|
||||
h = 0;
|
||||
} else if (left > containerWidth) {
|
||||
h = 360;
|
||||
} else {
|
||||
percent = left * 100 / containerWidth;
|
||||
h = (360 * percent / 100);
|
||||
}
|
||||
|
||||
if (this.colors.hsl.h !== h) {
|
||||
this.$emit('change', {
|
||||
h: h,
|
||||
s: this.colors.hsl.s,
|
||||
l: this.colors.hsl.l,
|
||||
a: this.colors.hsl.a,
|
||||
source: 'hsl'
|
||||
});
|
||||
}
|
||||
},
|
||||
handleMouseDown (e) {
|
||||
this.handleChange(e, true);
|
||||
window.addEventListener('mousemove', this.handleChange);
|
||||
window.addEventListener('mouseup', this.handleMouseUp);
|
||||
},
|
||||
handleMouseUp () {
|
||||
this.unbindEventListeners();
|
||||
},
|
||||
unbindEventListeners () {
|
||||
window.removeEventListener('mousemove', this.handleChange);
|
||||
window.removeEventListener('mouseup', this.handleMouseUp);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
98
src/components/color-picker/saturation.vue
Normal file
98
src/components/color-picker/saturation.vue
Normal file
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<div class="ivu-color-picker-saturation-wrapper">
|
||||
<div
|
||||
class="ivu-color-picker-saturation"
|
||||
:style="{background: bgColor}"
|
||||
ref="container"
|
||||
@mousedown="handleMouseDown">
|
||||
<div class="ivu-color-picker-saturation--white"></div>
|
||||
<div class="ivu-color-picker-saturation--black"></div>
|
||||
<div class="ivu-color-picker-saturation-pointer" :style="{top: pointerTop, left: pointerLeft}">
|
||||
<div class="ivu-color-picker-saturation-circle"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import throttle from '../../utils/throttle';
|
||||
|
||||
export default {
|
||||
name: 'Saturation',
|
||||
props: {
|
||||
value: Object
|
||||
},
|
||||
data () {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
colors () {
|
||||
return this.value;
|
||||
},
|
||||
bgColor () {
|
||||
return `hsl(${this.colors.hsv.h}, 100%, 50%)`;
|
||||
},
|
||||
pointerTop () {
|
||||
return (-(this.colors.hsv.v * 100) + 1) + 100 + '%';
|
||||
},
|
||||
pointerLeft () {
|
||||
return this.colors.hsv.s * 100 + '%';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
throttle: throttle((fn, data) => {fn(data);}, 20,
|
||||
{
|
||||
'leading': true,
|
||||
'trailing': false
|
||||
}),
|
||||
handleChange (e, skip) {
|
||||
!skip && e.preventDefault();
|
||||
const container = this.$refs.container;
|
||||
const containerWidth = container.clientWidth;
|
||||
const containerHeight = container.clientHeight;
|
||||
const xOffset = container.getBoundingClientRect().left + window.pageXOffset;
|
||||
const yOffset = container.getBoundingClientRect().top + window.pageYOffset;
|
||||
const pageX = e.pageX || (e.touches ? e.touches[0].pageX : 0);
|
||||
const pageY = e.pageY || (e.touches ? e.touches[0].pageY : 0);
|
||||
let left = pageX - xOffset;
|
||||
let top = pageY - yOffset;
|
||||
if (left < 0) {
|
||||
left = 0;
|
||||
} else if (left > containerWidth) {
|
||||
left = containerWidth;
|
||||
} else if (top < 0) {
|
||||
top = 0;
|
||||
} else if (top > containerHeight) {
|
||||
top = containerHeight;
|
||||
}
|
||||
const saturation = left / containerWidth;
|
||||
let bright = -(top / containerHeight) + 1;
|
||||
bright = bright > 0 ? bright : 0;
|
||||
bright = bright > 1 ? 1 : bright;
|
||||
this.throttle(this.onChange, {
|
||||
h: this.colors.hsv.h,
|
||||
s: saturation,
|
||||
v: bright,
|
||||
a: this.colors.hsv.a,
|
||||
source: 'hsva'
|
||||
});
|
||||
},
|
||||
onChange (param) {
|
||||
this.$emit('change', param);
|
||||
},
|
||||
handleMouseDown () {
|
||||
// this.handleChange(e, true)
|
||||
window.addEventListener('mousemove', this.handleChange);
|
||||
window.addEventListener('mouseup', this.handleChange);
|
||||
window.addEventListener('mouseup', this.handleMouseUp);
|
||||
},
|
||||
handleMouseUp () {
|
||||
this.unbindEventListeners();
|
||||
},
|
||||
unbindEventListeners () {
|
||||
window.removeEventListener('mousemove', this.handleChange);
|
||||
window.removeEventListener('mouseup', this.handleChange);
|
||||
window.removeEventListener('mouseup', this.handleMouseUp);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -24,30 +24,31 @@
|
|||
}
|
||||
|
||||
&-picker{
|
||||
padding: 8px 8px 0;
|
||||
padding: 4px 8px 0;
|
||||
&-panel{
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
margin: 0 auto;
|
||||
background: #47cb89;
|
||||
border-radius: 50%;
|
||||
box-sizing: initial;
|
||||
position: relative;
|
||||
}
|
||||
&-hue-slider{
|
||||
|
||||
}
|
||||
&-alpha-slider{
|
||||
|
||||
&-hue-slider, &-alpha-slider{
|
||||
height: 10px;
|
||||
margin-top: 8px;
|
||||
position: relative;
|
||||
}
|
||||
&-colors{
|
||||
margin-top: 8px;
|
||||
overflow: hidden;
|
||||
span{
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
float: left;
|
||||
em{
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 2px;
|
||||
cursor: pointer;
|
||||
border-radius: 2px;
|
||||
box-shadow: inset 0 0 0 1px rgba(0,0,0,.15);
|
||||
|
@ -58,4 +59,121 @@
|
|||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&-saturation{
|
||||
&-wrapper{
|
||||
width: 100%;
|
||||
padding-bottom: 75%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
&, &--white, &--black{
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
&--white{
|
||||
background: linear-gradient(to right, #fff, rgba(255,255,255,0));
|
||||
}
|
||||
&--black{
|
||||
background: linear-gradient(to top, #000, rgba(0,0,0,0));
|
||||
}
|
||||
&-pointer{
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
}
|
||||
&-circle{
|
||||
cursor: head;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
box-shadow: 0 0 0 1.5px #fff, inset 0 0 1px 1px rgba(0,0,0,.3), 0 0 1px 2px rgba(0,0,0,.4);
|
||||
border-radius: 50%;
|
||||
transform: translate(-2px, -2px);
|
||||
}
|
||||
}
|
||||
|
||||
&-hue{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border-radius: 2px;
|
||||
background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
|
||||
&-container{
|
||||
cursor: pointer;
|
||||
margin: 0 2px;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
&-pointer{
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
}
|
||||
&-picker{
|
||||
cursor: pointer;
|
||||
margin-top: 1px;
|
||||
width: 4px;
|
||||
border-radius: 1px;
|
||||
height: 8px;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, .6);
|
||||
background: #fff;
|
||||
transform: translateX(-2px);
|
||||
}
|
||||
}
|
||||
|
||||
&-alpha{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
&-checkboard-wrap{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
&-checkerboard{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
|
||||
}
|
||||
&-gradient{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
&-container{
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
height: 100%;
|
||||
margin: 0 3px;
|
||||
}
|
||||
&-pointer{
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
}
|
||||
&-picker{
|
||||
cursor: pointer;
|
||||
width: 4px;
|
||||
border-radius: 1px;
|
||||
height: 8px;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, .6);
|
||||
background: #fff;
|
||||
margin-top: 1px;
|
||||
transform: translateX(-2px);
|
||||
}
|
||||
}
|
||||
}
|
439
src/utils/throttle.js
Normal file
439
src/utils/throttle.js
Normal file
|
@ -0,0 +1,439 @@
|
|||
/**
|
||||
* lodash (Custom Build) <https://lodash.com/>
|
||||
* Build: `lodash modularize exports="npm" -o ./`
|
||||
* Copyright jQuery Foundation and other contributors <https://jquery.org/>
|
||||
* Released under MIT license <https://lodash.com/license>
|
||||
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
|
||||
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||
*/
|
||||
|
||||
/** Used as the `TypeError` message for "Functions" methods. */
|
||||
var FUNC_ERROR_TEXT = 'Expected a function';
|
||||
|
||||
/** Used as references for various `Number` constants. */
|
||||
var NAN = 0 / 0;
|
||||
|
||||
/** `Object#toString` result references. */
|
||||
var symbolTag = '[object Symbol]';
|
||||
|
||||
/** Used to match leading and trailing whitespace. */
|
||||
var reTrim = /^\s+|\s+$/g;
|
||||
|
||||
/** Used to detect bad signed hexadecimal string values. */
|
||||
var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
|
||||
|
||||
/** Used to detect binary string values. */
|
||||
var reIsBinary = /^0b[01]+$/i;
|
||||
|
||||
/** Used to detect octal string values. */
|
||||
var reIsOctal = /^0o[0-7]+$/i;
|
||||
|
||||
/** Built-in method references without a dependency on `root`. */
|
||||
var freeParseInt = parseInt;
|
||||
|
||||
/** Detect free variable `global` from Node.js. */
|
||||
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
|
||||
|
||||
/** Detect free variable `self`. */
|
||||
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
|
||||
|
||||
/** Used as a reference to the global object. */
|
||||
var root = freeGlobal || freeSelf || Function('return this')();
|
||||
|
||||
/** Used for built-in method references. */
|
||||
var objectProto = Object.prototype;
|
||||
|
||||
/**
|
||||
* Used to resolve the
|
||||
* [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
|
||||
* of values.
|
||||
*/
|
||||
var objectToString = objectProto.toString;
|
||||
|
||||
/* Built-in method references for those with the same name as other `lodash` methods. */
|
||||
var nativeMax = Math.max,
|
||||
nativeMin = Math.min;
|
||||
|
||||
/**
|
||||
* Gets the timestamp of the number of milliseconds that have elapsed since
|
||||
* the Unix epoch (1 January 1970 00:00:00 UTC).
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @since 2.4.0
|
||||
* @category Date
|
||||
* @returns {number} Returns the timestamp.
|
||||
* @example
|
||||
*
|
||||
* _.defer(function(stamp) {
|
||||
* console.log(_.now() - stamp);
|
||||
* }, _.now());
|
||||
* // => Logs the number of milliseconds it took for the deferred invocation.
|
||||
*/
|
||||
var now = function() {
|
||||
return root.Date.now();
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a debounced function that delays invoking `func` until after `wait`
|
||||
* milliseconds have elapsed since the last time the debounced function was
|
||||
* invoked. The debounced function comes with a `cancel` method to cancel
|
||||
* delayed `func` invocations and a `flush` method to immediately invoke them.
|
||||
* Provide `options` to indicate whether `func` should be invoked on the
|
||||
* leading and/or trailing edge of the `wait` timeout. The `func` is invoked
|
||||
* with the last arguments provided to the debounced function. Subsequent
|
||||
* calls to the debounced function return the result of the last `func`
|
||||
* invocation.
|
||||
*
|
||||
* **Note:** If `leading` and `trailing` options are `true`, `func` is
|
||||
* invoked on the trailing edge of the timeout only if the debounced function
|
||||
* is invoked more than once during the `wait` timeout.
|
||||
*
|
||||
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
|
||||
* until to the next tick, similar to `setTimeout` with a timeout of `0`.
|
||||
*
|
||||
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
|
||||
* for details over the differences between `_.debounce` and `_.throttle`.
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @since 0.1.0
|
||||
* @category Function
|
||||
* @param {Function} func The function to debounce.
|
||||
* @param {number} [wait=0] The number of milliseconds to delay.
|
||||
* @param {Object} [options={}] The options object.
|
||||
* @param {boolean} [options.leading=false]
|
||||
* Specify invoking on the leading edge of the timeout.
|
||||
* @param {number} [options.maxWait]
|
||||
* The maximum time `func` is allowed to be delayed before it's invoked.
|
||||
* @param {boolean} [options.trailing=true]
|
||||
* Specify invoking on the trailing edge of the timeout.
|
||||
* @returns {Function} Returns the new debounced function.
|
||||
* @example
|
||||
*
|
||||
* // Avoid costly calculations while the window size is in flux.
|
||||
* jQuery(window).on('resize', _.debounce(calculateLayout, 150));
|
||||
*
|
||||
* // Invoke `sendMail` when clicked, debouncing subsequent calls.
|
||||
* jQuery(element).on('click', _.debounce(sendMail, 300, {
|
||||
* 'leading': true,
|
||||
* 'trailing': false
|
||||
* }));
|
||||
*
|
||||
* // Ensure `batchLog` is invoked once after 1 second of debounced calls.
|
||||
* var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
|
||||
* var source = new EventSource('/stream');
|
||||
* jQuery(source).on('message', debounced);
|
||||
*
|
||||
* // Cancel the trailing debounced invocation.
|
||||
* jQuery(window).on('popstate', debounced.cancel);
|
||||
*/
|
||||
function debounce(func, wait, options) {
|
||||
var lastArgs,
|
||||
lastThis,
|
||||
maxWait,
|
||||
result,
|
||||
timerId,
|
||||
lastCallTime,
|
||||
lastInvokeTime = 0,
|
||||
leading = false,
|
||||
maxing = false,
|
||||
trailing = true;
|
||||
|
||||
if (typeof func != 'function') {
|
||||
throw new TypeError(FUNC_ERROR_TEXT);
|
||||
}
|
||||
wait = toNumber(wait) || 0;
|
||||
if (isObject(options)) {
|
||||
leading = !!options.leading;
|
||||
maxing = 'maxWait' in options;
|
||||
maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
|
||||
trailing = 'trailing' in options ? !!options.trailing : trailing;
|
||||
}
|
||||
|
||||
function invokeFunc(time) {
|
||||
var args = lastArgs,
|
||||
thisArg = lastThis;
|
||||
|
||||
lastArgs = lastThis = undefined;
|
||||
lastInvokeTime = time;
|
||||
result = func.apply(thisArg, args);
|
||||
return result;
|
||||
}
|
||||
|
||||
function leadingEdge(time) {
|
||||
// Reset any `maxWait` timer.
|
||||
lastInvokeTime = time;
|
||||
// Start the timer for the trailing edge.
|
||||
timerId = setTimeout(timerExpired, wait);
|
||||
// Invoke the leading edge.
|
||||
return leading ? invokeFunc(time) : result;
|
||||
}
|
||||
|
||||
function remainingWait(time) {
|
||||
var timeSinceLastCall = time - lastCallTime,
|
||||
timeSinceLastInvoke = time - lastInvokeTime,
|
||||
result = wait - timeSinceLastCall;
|
||||
|
||||
return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;
|
||||
}
|
||||
|
||||
function shouldInvoke(time) {
|
||||
var timeSinceLastCall = time - lastCallTime,
|
||||
timeSinceLastInvoke = time - lastInvokeTime;
|
||||
|
||||
// Either this is the first call, activity has stopped and we're at the
|
||||
// trailing edge, the system time has gone backwards and we're treating
|
||||
// it as the trailing edge, or we've hit the `maxWait` limit.
|
||||
return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
|
||||
(timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
|
||||
}
|
||||
|
||||
function timerExpired() {
|
||||
var time = now();
|
||||
if (shouldInvoke(time)) {
|
||||
return trailingEdge(time);
|
||||
}
|
||||
// Restart the timer.
|
||||
timerId = setTimeout(timerExpired, remainingWait(time));
|
||||
}
|
||||
|
||||
function trailingEdge(time) {
|
||||
timerId = undefined;
|
||||
|
||||
// Only invoke if we have `lastArgs` which means `func` has been
|
||||
// debounced at least once.
|
||||
if (trailing && lastArgs) {
|
||||
return invokeFunc(time);
|
||||
}
|
||||
lastArgs = lastThis = undefined;
|
||||
return result;
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
if (timerId !== undefined) {
|
||||
clearTimeout(timerId);
|
||||
}
|
||||
lastInvokeTime = 0;
|
||||
lastArgs = lastCallTime = lastThis = timerId = undefined;
|
||||
}
|
||||
|
||||
function flush() {
|
||||
return timerId === undefined ? result : trailingEdge(now());
|
||||
}
|
||||
|
||||
function debounced() {
|
||||
var time = now(),
|
||||
isInvoking = shouldInvoke(time);
|
||||
|
||||
lastArgs = arguments;
|
||||
lastThis = this;
|
||||
lastCallTime = time;
|
||||
|
||||
if (isInvoking) {
|
||||
if (timerId === undefined) {
|
||||
return leadingEdge(lastCallTime);
|
||||
}
|
||||
if (maxing) {
|
||||
// Handle invocations in a tight loop.
|
||||
timerId = setTimeout(timerExpired, wait);
|
||||
return invokeFunc(lastCallTime);
|
||||
}
|
||||
}
|
||||
if (timerId === undefined) {
|
||||
timerId = setTimeout(timerExpired, wait);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
debounced.cancel = cancel;
|
||||
debounced.flush = flush;
|
||||
return debounced;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a throttled function that only invokes `func` at most once per
|
||||
* every `wait` milliseconds. The throttled function comes with a `cancel`
|
||||
* method to cancel delayed `func` invocations and a `flush` method to
|
||||
* immediately invoke them. Provide `options` to indicate whether `func`
|
||||
* should be invoked on the leading and/or trailing edge of the `wait`
|
||||
* timeout. The `func` is invoked with the last arguments provided to the
|
||||
* throttled function. Subsequent calls to the throttled function return the
|
||||
* result of the last `func` invocation.
|
||||
*
|
||||
* **Note:** If `leading` and `trailing` options are `true`, `func` is
|
||||
* invoked on the trailing edge of the timeout only if the throttled function
|
||||
* is invoked more than once during the `wait` timeout.
|
||||
*
|
||||
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
|
||||
* until to the next tick, similar to `setTimeout` with a timeout of `0`.
|
||||
*
|
||||
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
|
||||
* for details over the differences between `_.throttle` and `_.debounce`.
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @since 0.1.0
|
||||
* @category Function
|
||||
* @param {Function} func The function to throttle.
|
||||
* @param {number} [wait=0] The number of milliseconds to throttle invocations to.
|
||||
* @param {Object} [options={}] The options object.
|
||||
* @param {boolean} [options.leading=true]
|
||||
* Specify invoking on the leading edge of the timeout.
|
||||
* @param {boolean} [options.trailing=true]
|
||||
* Specify invoking on the trailing edge of the timeout.
|
||||
* @returns {Function} Returns the new throttled function.
|
||||
* @example
|
||||
*
|
||||
* // Avoid excessively updating the position while scrolling.
|
||||
* jQuery(window).on('scroll', _.throttle(updatePosition, 100));
|
||||
*
|
||||
* // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
|
||||
* var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
|
||||
* jQuery(element).on('click', throttled);
|
||||
*
|
||||
* // Cancel the trailing throttled invocation.
|
||||
* jQuery(window).on('popstate', throttled.cancel);
|
||||
*/
|
||||
function throttle(func, wait, options) {
|
||||
var leading = true,
|
||||
trailing = true;
|
||||
|
||||
if (typeof func != 'function') {
|
||||
throw new TypeError(FUNC_ERROR_TEXT);
|
||||
}
|
||||
if (isObject(options)) {
|
||||
leading = 'leading' in options ? !!options.leading : leading;
|
||||
trailing = 'trailing' in options ? !!options.trailing : trailing;
|
||||
}
|
||||
return debounce(func, wait, {
|
||||
'leading': leading,
|
||||
'maxWait': wait,
|
||||
'trailing': trailing
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if `value` is the
|
||||
* [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
|
||||
* of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @since 0.1.0
|
||||
* @category Lang
|
||||
* @param {*} value The value to check.
|
||||
* @returns {boolean} Returns `true` if `value` is an object, else `false`.
|
||||
* @example
|
||||
*
|
||||
* _.isObject({});
|
||||
* // => true
|
||||
*
|
||||
* _.isObject([1, 2, 3]);
|
||||
* // => true
|
||||
*
|
||||
* _.isObject(_.noop);
|
||||
* // => true
|
||||
*
|
||||
* _.isObject(null);
|
||||
* // => false
|
||||
*/
|
||||
function isObject(value) {
|
||||
var type = typeof value;
|
||||
return !!value && (type == 'object' || type == 'function');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if `value` is object-like. A value is object-like if it's not `null`
|
||||
* and has a `typeof` result of "object".
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @since 4.0.0
|
||||
* @category Lang
|
||||
* @param {*} value The value to check.
|
||||
* @returns {boolean} Returns `true` if `value` is object-like, else `false`.
|
||||
* @example
|
||||
*
|
||||
* _.isObjectLike({});
|
||||
* // => true
|
||||
*
|
||||
* _.isObjectLike([1, 2, 3]);
|
||||
* // => true
|
||||
*
|
||||
* _.isObjectLike(_.noop);
|
||||
* // => false
|
||||
*
|
||||
* _.isObjectLike(null);
|
||||
* // => false
|
||||
*/
|
||||
function isObjectLike(value) {
|
||||
return !!value && typeof value == 'object';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if `value` is classified as a `Symbol` primitive or object.
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @since 4.0.0
|
||||
* @category Lang
|
||||
* @param {*} value The value to check.
|
||||
* @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
|
||||
* @example
|
||||
*
|
||||
* _.isSymbol(Symbol.iterator);
|
||||
* // => true
|
||||
*
|
||||
* _.isSymbol('abc');
|
||||
* // => false
|
||||
*/
|
||||
function isSymbol(value) {
|
||||
return typeof value == 'symbol' ||
|
||||
(isObjectLike(value) && objectToString.call(value) == symbolTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts `value` to a number.
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @since 4.0.0
|
||||
* @category Lang
|
||||
* @param {*} value The value to process.
|
||||
* @returns {number} Returns the number.
|
||||
* @example
|
||||
*
|
||||
* _.toNumber(3.2);
|
||||
* // => 3.2
|
||||
*
|
||||
* _.toNumber(Number.MIN_VALUE);
|
||||
* // => 5e-324
|
||||
*
|
||||
* _.toNumber(Infinity);
|
||||
* // => Infinity
|
||||
*
|
||||
* _.toNumber('3.2');
|
||||
* // => 3.2
|
||||
*/
|
||||
function toNumber(value) {
|
||||
if (typeof value == 'number') {
|
||||
return value;
|
||||
}
|
||||
if (isSymbol(value)) {
|
||||
return NAN;
|
||||
}
|
||||
if (isObject(value)) {
|
||||
var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
|
||||
value = isObject(other) ? (other + '') : other;
|
||||
}
|
||||
if (typeof value != 'string') {
|
||||
return value === 0 ? value : +value;
|
||||
}
|
||||
value = value.replace(reTrim, '');
|
||||
var isBinary = reIsBinary.test(value);
|
||||
return (isBinary || reIsOctal.test(value))
|
||||
? freeParseInt(value.slice(2), isBinary ? 2 : 8)
|
||||
: (reIsBadHex.test(value) ? NAN : +value);
|
||||
}
|
||||
|
||||
module.exports = throttle;
|
Loading…
Reference in a new issue