<!--本文件由FirstUI授权予新疆天衡创新研究院有限公司（手机号： 186 14    0  7254  9，身份证尾号：5A07X5）专用，请尊重知识产权，勿私下传播，违者追究法律责任。-->
<template>
	<view class="fui-circle__wrap" :style="{width: w+'px',height:w+'px'}">
		<!-- #ifdef APP-NVUE -->
		<gcanvas :ref="circleId" :style="{ width: w + 'px', height: w + 'px' }"></gcanvas>
		<!-- #endif -->
		<!-- #ifdef APP-VUE || H5 -->
		<canvas :start="percentage" :change:start="parse.init" :width="w" :change:width="parse.widthChange"
			:sw="strokeWidth" :change:sw="parse.widthChange" :data-width="w" :data-sw="strokeWidth" :data-lc="lineCap"
			:data-size="size" :data-percent="percentage" :data-color="color || primaryColor" :data-show="show"
			:data-ds="defaultShow" :data-background="background" :data-foreground="foreground || primaryColor"
			:data-gradient="gradient" :data-sa="sAngle" :data-ccw="counterclockwise" :data-speed="speed"
			:data-am="activeMode" :data-cid="circleId" :canvas-id="circleId" :class="[circleId]"
			:style="{width: w + 'px',height: w + 'px'}"></canvas>
		<!-- #endif -->

		<!-- #ifdef MP -->
		<!-- #ifdef MP-ALIPAY -->
		<canvas class="fui-circle__canvas" :canvas-id="circleId" :id="circleId"
			:style="{width: w*4 +'px',height:w*4 +'px'}"></canvas>
		<!-- #endif -->
		<!-- #ifndef MP-ALIPAY -->
		<!-- 此处加v-if是因为vue3下编译到头条/抖音等小程序动态id失效，需等uni-app官方调整 -->
		<canvas class="fui-circle__canvas" :canvas-id="circleId" :id="circleId" :style="{width: w +'px',height:w +'px'}"
			v-if="circleId"></canvas>
		<!-- #endif -->
		<!-- #endif -->
		<view class="fui-circle__inner">
			<slot></slot>
		</view>
	</view>
</template>
<!-- #ifdef APP-VUE || H5 -->
<script module="parse" lang="renderjs">
	export default {
		methods: {
			widthChange(w, oldW, owner, ins) {
				let res = ins.getDataset();
				let percent = Number(res.percent);
				this.init(percent, percent, owner, ins)
			},
			init(percent, oldPercent, owner, ins) {
				let state = ins.getState();
				let res = ins.getDataset();
				const am = this.format(res.am);
				let start = am === 'backwards' ? 0 : (state.start || 0);
				start = start > percent ? 0 : start;
				if (!state.context || !state.canvas) {
					const width = res.width;
					let ele = `.${res.cid}>canvas`
					const canvas = document.querySelectorAll(this.format(ele))[0];
					if (!canvas) return;
					const ctx = canvas.getContext('2d');
					state.context = ctx;
					state.canvas = canvas;
					this.drawCircle(start, ctx, canvas, percent, res, state, owner);
				} else {
					this.drawCircle(start, state.context, state.canvas, percent, res, state, owner);
				}
			},
			drawDefaultCircle(ctx, canvas, res) {
				let sa = Number(res.sa) * Math.PI
				let eAngle = Math.PI * 2 + sa;
				this.drawArc(ctx, eAngle, this.format(res.background), res);
			},
			drawPercent(ctx, percent, res) {
				ctx.save();
				ctx.beginPath();
				ctx.fillStyle = this.format(res.color);
				ctx.font = res.size + "px Arial";
				ctx.textAlign = "center";
				ctx.textBaseline = "middle";
				let radius = res.width / 2;
				percent = this.bool(res.ccw) ? 100 - percent : percent;
				ctx.fillText(percent + '%', radius, radius);
				ctx.stroke();
				ctx.restore();
			},
			drawCircle(start, ctx, canvas, percent, res, state, owner) {
				if (!ctx || !canvas) return;
				let _this = this
				let gradient = res.foreground;
				if (this.format(res.gradient)) {
					gradient = ctx.createLinearGradient(0, 0, Number(res.width), 0);
					gradient.addColorStop(0, this.format(res.gradient));
					gradient.addColorStop(1, this.format(res.foreground));
				}
				let requestId = null
				let renderLoop = () => {
					drawFrame((res) => {
						if (res) {
							requestId = requestAnimationFrame(renderLoop)
						} else {
							setTimeout(() => {
								cancelAnimationFrame(requestId)
								requestId = null;
								renderLoop = null;
							}, 20)
						}
					})
				}
				renderLoop()

				function drawFrame(callback) {
					ctx.clearRect(0, 0, canvas.width, canvas.width);
					if (_this.bool(res.ds)) {
						_this.drawDefaultCircle(ctx, canvas, res)
					}
					if (_this.bool(res.show)) {
						_this.drawPercent(ctx, start, res);
					}
					let isEnd = percent === 0 || (_this.bool(res.ccw) && start === 100);
					if (!isEnd) {
						let sa = Number(res.sa) * Math.PI
						let eAngle = ((2 * Math.PI) / 100) * start + sa;
						_this.drawArc(ctx, eAngle, gradient, res);
					}
					owner.callMethod('change', {
						percent: start
					})
					if (start >= percent) {
						state.start = start;
						owner.callMethod('end', {
							canvasId: _this.format(res.cid)
						})
						callback && callback(false)
					} else {
						let num = start + Number(res.speed)
						start = num > percent ? percent : num;
						callback && callback(true)
					}
				}
			},
			//创建弧线
			drawArc(ctx, eAngle, strokeStyle, res) {
				ctx.save();
				ctx.beginPath();
				ctx.lineCap = this.format(res.lc);
				ctx.lineWidth = Number(res.sw);
				ctx.strokeStyle = strokeStyle;
				let radius = res.width / 2;
				let sa = Number(res.sa) * Math.PI
				ctx.arc(radius, radius, radius - Number(res.sw), sa, eAngle, this.bool(res.ccw));
				ctx.stroke();
				ctx.closePath();
				ctx.restore();
			},
			format(str) {
				if (!str) return str;
				return str.replace(/\"/g, "");
			},
			bool(str) {
				return str === 'true' || str == true ? true : false
			}
		}
	}
</script>
<!-- #endif -->
<script>
	// #ifdef APP-NVUE
	import {
		enable,
		WeexBridge
	} from './gcanvas/index.js';
	// #endif
	//注意：app-nvue端canvas性能不如app-vue
	import mpjs from './mpjs.js'
	import render from './render.js'
	import nvue from './nvue.js'
	// #ifdef MP-WEIXIN
	const circleId = `fui_cc_${Math.ceil(Math.random() * 10e5).toString(36)}`
	// #endif
	export default {
		name: "fui-circle",
		mixins: [mpjs, render, nvue],
		emits: ['change', 'end'],
		props: {
			//进度百分比 0~100
			percent: {
				type: [Number, String],
				default: 0
			},
			width: {
				type: [Number, String],
				default: 120
			},
			//背景填充颜色，仅nvue ios有效
			fillStyle: {
				type: String,
				default: '#FFFFFF'
			},
			//进度环的宽度，单位 px
			strokeWidth: {
				type: [Number, String],
				default: 4
			},
			//进度环的线头样式，可选值为 square、round、butt
			lineCap: {
				type: String,
				default: 'round'
			},
			//圆环进度字体大小,px
			size: {
				type: [Number, String],
				default: 12
			},
			//圆环进度字体颜色
			color: {
				type: String,
				default: ''
			},
			//是否显示进度文字
			show: {
				type: Boolean,
				default: true
			},
			//默认背景颜色
			background: {
				type: String,
				default: '#CCCCCC'
			},
			//是否显示默认背景色
			defaultShow: {
				type: Boolean,
				default: true
			},
			//默认前景颜色
			foreground: {
				type: String,
				default: ''
			},
			//进度条渐变颜色，结合foreground使用
			gradient: {
				type: String,
				default: ''
			},
			//起始弧度，单位弧度 实际  Math.PI * sAngle
			sAngle: {
				type: Number,
				default: 0
			},
			//指定弧度的方向是逆时针还是顺时针。默认是false，即顺时针
			counterclockwise: {
				type: Boolean,
				default: false
			},
			//动画执行速度，值越大动画越快（1~100）
			speed: {
				type: [Number, String],
				default: 1
			},
			//backwards: 动画从头播；forwards：动画从上次结束点接着播
			activeMode: {
				type: String,
				default: 'forwards'
			}
		},
		watch: {
			width(val) {
				this.initWidth(val)
			}
		},
		computed: {
			primaryColor() {
				const app = uni && uni.$fui && uni.$fui.color;
				return (app && app.primary) || '#465CFF';
			}
		},
		data() {
			// #ifndef MP-WEIXIN
			const circleId = `fui_cc_${Math.ceil(Math.random() * 10e5).toString(36)}`
			// #endif
			return {
				circleId: circleId,
				w: 60
			};
		},
		created() {
			// #ifdef APP-NVUE
			this.context = null
			// #endif
			this.initWidth(this.width)
		},
		// #ifdef APP-NVUE
		mounted() {
			//以下代码写在nvue.js中 android端可能会出现canvas大小不受控制
			setTimeout(() => {
				let ganvas = this.$refs[this.circleId];
				let canvas = enable(ganvas, {
					bridge: WeexBridge
				});
				this.context = canvas.getContext('2d');
			}, 50);
		},
		// #endif
		methods: {
			initWidth(val) {
				val = uni.upx2px(Number(val) || 120)
				this.w = val % 2 === 0 ? val : val + 1
			}
		}
	}
</script>

<style scoped>
	.fui-circle__wrap {
		position: relative
	}

	.fui-circle__inner {
		/* #ifndef APP-NVUE */
		width: 100%;
		display: flex;
		z-index: 10;
		/* #endif */
		text-align: center;
		position: absolute;
		left: 0;
		right: 0;
		top: 0;
		bottom: 0;
		flex-direction: row;
		align-items: center;
		justify-content: center;
	}

	/* #ifdef MP */
	.fui-circle__canvas {
		position: absolute;
		left: 0;
		top: 0;
		z-index: 5;
		/* #ifdef MP-ALIPAY */
		zoom: 0.25;
		/* #endif */
	}

	/* #endif */
</style>