refactor(Circle): use composition api

This commit is contained in:
chenjiahan 2020-08-24 20:18:56 +08:00
parent 8f978addd4
commit ded5aa431e

View File

@ -1,11 +1,10 @@
import { watch, computed } from 'vue';
import { createNamespace, isObject, addUnit } from '../utils'; import { createNamespace, isObject, addUnit } from '../utils';
import { raf, cancelRaf } from '../utils/dom/raf'; import { raf, cancelRaf } from '../utils/dom/raf';
import { BLUE, WHITE } from '../utils/constant'; import { BLUE, WHITE } from '../utils/constant';
const [createComponent, bem] = createNamespace('circle'); const [createComponent, bem] = createNamespace('circle');
const PERIMETER = 3140;
let uid = 0; let uid = 0;
function format(rate) { function format(rate) {
@ -63,123 +62,122 @@ export default createComponent({
emits: ['update:currentRate'], emits: ['update:currentRate'],
beforeCreate() { setup(props, { emit, slots }) {
this.uid = `van-circle-gradient-${uid++}`; const id = `van-circle-${uid++}`;
},
computed: { const viewBoxSize = computed(() => +props.strokeWidth + 1000);
style() {
const size = addUnit(this.size); const path = computed(() => getPath(props.clockwise, viewBoxSize.value));
const rootStyle = computed(() => {
const size = addUnit(props.size);
return { return {
width: size, width: size,
height: size, height: size,
}; };
}, });
path() { watch(
return getPath(this.clockwise, this.viewBoxSize); computed(() => props.rate),
}, (rate) => {
let rafId;
const startTime = Date.now();
const startRate = props.currentRate;
const endRate = format(rate);
const duration = Math.abs(((startRate - endRate) * 1000) / props.speed);
viewBoxSize() { const animate = () => {
return +this.strokeWidth + 1000; const now = Date.now();
}, const progress = Math.min((now - startTime) / duration, 1);
const rate = progress * (endRate - startRate) + startRate;
layerStyle() { emit('update:currentRate', format(parseFloat(rate.toFixed(1))));
const offset = (PERIMETER * this.currentRate) / 100;
return { if (endRate > startRate ? rate < endRate : rate > endRate) {
stroke: `${this.color}`, rafId = raf(animate);
strokeWidth: `${+this.strokeWidth + 1}px`, }
strokeLinecap: this.strokeLinecap, };
if (props.speed) {
cancelRaf(rafId);
rafId = raf(animate);
} else {
emit('update:currentRate', endRate);
}
}
);
const renderHover = () => {
const style = {
fill: props.fill,
stroke: props.layerColor,
strokeWidth: `${props.strokeWidth}px`,
};
return <path class={bem('hover')} style={style} d={path.value} />;
};
const renderLayer = () => {
const PERIMETER = 3140;
const { color, strokeWidth, currentRate, strokeLinecap } = props;
const offset = (PERIMETER * currentRate) / 100;
const style = {
stroke: `${color}`,
strokeWidth: `${+strokeWidth + 1}px`,
strokeLinecap,
strokeDasharray: `${offset}px ${PERIMETER}px`, strokeDasharray: `${offset}px ${PERIMETER}px`,
}; };
},
hoverStyle() { return (
return { <path
fill: `${this.fill}`, d={path.value}
stroke: `${this.layerColor}`, style={style}
strokeWidth: `${this.strokeWidth}px`, class={bem('layer')}
stroke={isObject(color) ? `url(#${id})` : color}
/>
);
}; };
},
gradient() { const renderGradient = () => {
return isObject(this.color); const { color } = props;
},
LinearGradient() { if (!isObject(color)) {
if (!this.gradient) {
return; return;
} }
const Stops = Object.keys(this.color) const Stops = Object.keys(color)
.sort((a, b) => parseFloat(a) - parseFloat(b)) .sort((a, b) => parseFloat(a) - parseFloat(b))
.map((key, index) => ( .map((key, index) => (
<stop key={index} offset={key} stop-color={this.color[key]} /> <stop key={index} offset={key} stop-color={color[key]} />
)); ));
return ( return (
<defs> <defs>
<linearGradient id={this.uid} x1="100%" y1="0%" x2="0%" y2="0%"> <linearGradient id={id} x1="100%" y1="0%" x2="0%" y2="0%">
{Stops} {Stops}
</linearGradient> </linearGradient>
</defs> </defs>
); );
}, };
},
watch: { const renderText = () => {
rate: { if (slots.default) {
handler(rate) { return slots.default();
this.startTime = Date.now();
this.startRate = this.currentRate;
this.endRate = format(rate);
this.increase = this.endRate > this.startRate;
this.duration = Math.abs(
((this.startRate - this.endRate) * 1000) / this.speed
);
if (this.speed) {
cancelRaf(this.rafId);
this.rafId = raf(this.animate);
} else {
this.$emit('update:currentRate', this.endRate);
} }
},
immediate: true,
},
},
methods: { if (props.text) {
animate() { return <div class={bem('text')}>{props.text}</div>;
const now = Date.now();
const progress = Math.min((now - this.startTime) / this.duration, 1);
const rate = progress * (this.endRate - this.startRate) + this.startRate;
this.$emit('update:currentRate', format(parseFloat(rate.toFixed(1))));
if (this.increase ? rate < this.endRate : rate > this.endRate) {
this.rafId = raf(this.animate);
} }
}, };
},
render() { return () => (
return ( <div class={bem()} style={rootStyle.value}>
<div class={bem()} style={this.style}> <svg viewBox={`0 0 ${viewBoxSize.value} ${viewBoxSize.value}`}>
<svg viewBox={`0 0 ${this.viewBoxSize} ${this.viewBoxSize}`}> {renderGradient()}
{this.LinearGradient} {renderHover()}
<path class={bem('hover')} style={this.hoverStyle} d={this.path} /> {renderLayer()}
<path
d={this.path}
class={bem('layer')}
style={this.layerStyle}
stroke={this.gradient ? `url(#${this.uid})` : this.color}
/>
</svg> </svg>
{this.$slots.default {renderText()}
? this.$slots.default()
: this.text && <div class={bem('text')}>{this.text}</div>}
</div> </div>
); );
}, },