Commit 36e4b001 authored by Friends233's avatar Friends233

环形倒计时

parent 73980bf9
...@@ -6,7 +6,12 @@ const dcm = '202.' + projectId + '.0.0'; ...@@ -6,7 +6,12 @@ const dcm = '202.' + projectId + '.0.0';
const domain = '//embedlog.duiba.com.cn'; const domain = '//embedlog.duiba.com.cn';
md.prepare({ appId }) md.prepare({ appId })
const exposure = [1, 3], click = [2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24] /** 单曝光 */
const exposure = []
/** 单点击 */
const click = []
/** 既曝光也点击 */
const all = []
const mdList = Array(24).fill('').map((_, index) => { const mdList = Array(24).fill('').map((_, index) => {
const ci = index + 1 const ci = index + 1
...@@ -16,8 +21,8 @@ const mdList = Array(24).fill('').map((_, index) => { ...@@ -16,8 +21,8 @@ const mdList = Array(24).fill('').map((_, index) => {
dpm: `${appId}.110.${ci}.1`, dpm: `${appId}.110.${ci}.1`,
dcm, dcm,
}, },
logExposure: exposure.includes(ci), logExposure: [...exposure, ...all].includes(ci),
logClick: click.includes(ci) logClick: [...click, ...all].includes(ci)
} }
}) })
md.registerBuriedPoints(mdList); md.registerBuriedPoints(mdList);
......
...@@ -28,3 +28,5 @@ ...@@ -28,3 +28,5 @@
**普通轮播(奖品和文字轮播):[SwiperC](http://gitlab2.dui88.com/wangqiang1/best-code/blob/master/src/pages/SwiperC/index.jsx)** **普通轮播(奖品和文字轮播):[SwiperC](http://gitlab2.dui88.com/wangqiang1/best-code/blob/master/src/pages/SwiperC/index.jsx)**
**环形倒计时:**[**Circel**](http://gitlab2.dui88.com/wangqiang1/best-code/blob/master/src/components/Circel/index.jsx)
import { observer } from "mobx-react";
import React, { useEffect, useRef } from "react";
import './index.less'
/**
* 返回在页面中真实的 px 值
* @param {Number} designSize 设计稿上的像素值
* @returns
*/
function getTruthPx(designSize) {
const remUnit = parseFloat(document.documentElement.style.fontSize)
return designSize / 100 * remUnit
}
const Circel = ({ precent, className, lineWidth = 10, width = 300, height = 300, color = '#ffffff' }) => {
const num = 0
const ctx = useRef(null)
const leftCircelWrpStyle = {
width: `${width / 100 / 2}rem`,
height: `${height / 100}rem`,
borderRadius: `${width / 100}rem 0 0 ${width / 100}rem`,
// left: `${width / 100 / 2}rem`,
}
const leftCircelStyle = {
...leftCircelWrpStyle,
background: color,
}
const rightCircelWrpStyle = {
width: `${width / 100 / 2}rem`,
height: `${height / 100}rem`,
borderRadius: `0 ${width / 100}rem ${width / 100}rem 0`,
left: `${width / 100 / 2}rem`,
}
const rightCircelStyle = {
...rightCircelWrpStyle,
left: 0,
background: color,
}
return <div className={"circel-wrp " + className}>
<div className="mid-circel">
<div className="left-circel-wrp" style={leftCircelWrpStyle}>
<div style={leftCircelStyle} className="left-circel"></div>
</div>
<div className="right-circel-wrp" style={rightCircelWrpStyle}>
<div style={rightCircelStyle} className="right-circel"></div>
</div>
<div className="con-circel"></div>
</div>
<div className="circel-circel"></div>
</div>
}
export default observer(Circel)
\ No newline at end of file
@keyframes sector {
from {
transform: rotate(0deg);
}
to {
transform: rotate(180deg);
}
}
.circel-wrp {
width: 100%;
height: 100%;
// background-color: brown;
// border-radius: 50%;
position: absolute;
.circel-circel {
width: 98%;
height: 98%;
background-color: brown;
border-radius: 50%;
left: 1%;
top: 1%;
position: absolute;
z-index: 98;
}
.left-circel-wrp {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
overflow: hidden;
.left-circel {
// width: 300px;
// height: 150px;
position: absolute;
left: 0;
top: 0;
transform-origin: 100% 50%;
animation: sector 1s linear 1s forwards;
}
}
.right-circel-wrp {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
overflow: hidden;
.right-circel {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
transform-origin: 0 50%;
animation: sector 1s linear 0s forwards;
}
}
.mid-circel {
width: 100%;
height: 100%;
padding: 10px;
box-sizing: border-box;
position: absolute;
left: 0;
top: 0;
z-index: 99;
.con-circel {
width: 100%;
height: 100%;
background-color: #ffffff;
border-radius: 50%;
}
}
}
export const CD_EVENTS_TYPE = {
/** 开始 */
START: 'START',
/** 进行中 */
PENDING: 'PENDING',
/** 结束 */
END: 'END',
/** 停止 */
STOP: 'STOP'
}
export class CircelCountdown {
constructor(settings) {
// 设置默认参数
this.settings = {
...settings
};
// 步长
this.schedule = 0
/** 事件MAP */
this.eventsMap = {}
this.timer = null
this.oldTime = null
this.timeCopy = this.settings.time * 1000
this.stopTime = 0
}
init(opt) {
this.obj = document.getElementById(this.settings.id);
this.obj.width = this.settings.size;
this.obj.height = this.settings.size;
this.ctx = this.obj.getContext("2d");
extend(this.settings, opt);
this.drawBackground();
this.drawProcess();
this.drawAnimate();
this.drawInner();
this.strokeText(0);
this.settings?.auto && this.start();
}
// 绘制底色
drawBackground() {
this.drawCircle(0, 360, 0, this.settings.outerColor);
}
// 绘制进度条动画背景
drawProcess() {
this.drawCircle(0, 360, 4, this.settings.ringColor);
}
// 绘制倒计时
drawInner() {
this.drawCircle(0, 360, 23, this.settings.innerColor);
this.strokeBorder(this.settings.borderWidth);
}
// 绘制进度条动画
drawAnimate() {
// 旋转的角度
const deg = Math.PI / 180;
const v = this.schedule * 360, startAng = -90, endAng = -90 + v;
this.ctx.beginPath();
this.ctx.moveTo(this.settings.size / 2, this.settings.size / 2);
this.ctx.arc(this.settings.size / 2, this.settings.size / 2, this.settings.size / 2 - 3, startAng * deg, endAng * deg, false);
this.ctx.fillStyle = this.settings.scheduleColor;
this.ctx.fill();
this.ctx.closePath();
}
// 绘制边框
strokeBorder(borderWidth) {
this.ctx.lineWidth = borderWidth;
this.ctx.strokeStyle = this.settings.borderColor;
this.ctx.stroke();
}
// 绘制文字
strokeText(text) {
this.ctx.textAlign = "center";
this.ctx.textBaseline = "middle";
this.ctx.font = this.settings.fontSize + "px" + " microsoft yahei";
this.ctx.fillStyle = this.settings.fontColor;
this.ctx.fillText(text, this.settings.size / 2, this.settings.size / 2);
}
// 绘制圆
drawCircle(startAng, endAng, border, fillColor) {
const deg = Math.PI / 180;
this.ctx.beginPath();
this.ctx.arc(this.settings.size / 2, this.settings.size / 2, this.settings.size / 2 - border, startAng * deg, endAng * deg, false);
this.ctx.fillStyle = fillColor;
this.ctx.fill();
this.ctx.closePath();
}
// 进度条动画
start() {
if (!this.oldTime) {
this.oldTime = +new Date();
}
if (this.stopTime) {
this.oldTime += +new Date() - this.stopTime
}
const oldTime = this.oldTime
this.emit(CD_EVENTS_TYPE.START)
this.timer = setInterval(() => {
const allMs = this.settings.time * 1000, // 如30*1000=30 000ms
currentTime = +new Date();
// 步长=(当前的时间-过去的时间)/总秒数
const schedule = (currentTime - oldTime) / allMs;
this.schedule = schedule;
this.drawAll(schedule);
if (currentTime - oldTime >= allMs) {
// 重绘
this.drawBackground();
this.drawProcess();
this.drawAnimate();
this.drawInner();
this.stop()
this.emit(CD_EVENTS_TYPE.END)
}
}, 100);
}
// 停止
stop() {
this.stopTime = Date.now()
this.emit(CD_EVENTS_TYPE.STOP)
clearInterval(this.timer);
}
// 绘制所有
drawAll(schedule) {
schedule = schedule >= 1 ? 1 : schedule;
const text = parseInt(this.settings.time * (1 - schedule)) + 1;
// 清除画布
this.ctx.clearRect(0, 0, this.settings.size, this.settings.size);
this.drawBackground();
this.drawProcess();
this.drawAnimate();
this.drawInner();
this.strokeText(text);
}
// 绑定事件
on(events, callback) {
this.eventsMap[events] = callback
}
// 触发事件
emit(events) {
this.eventsMap?.[events]?.()
}
}
// 对象拷贝
function extend(obj1, obj2) {
for (const attr in obj2) {
obj1[attr] = obj2[attr];
}
}
\ No newline at end of file
import { observer } from "mobx-react";
import React, { useEffect, useRef } from "react";
import { CD_EVENTS_TYPE, CircelCountdown } from "./circel.js";
import './index.less'
/**
* 返回在页面中真实的 px 值
* @param {Number} designSize 设计稿上的像素值
* @returns
*/
function getTruthPx(designSize) {
const remUnit = parseFloat(document.documentElement.style.fontSize)
return designSize / 100 * remUnit
}
const Circel = React.forwardRef(({
className,
id = 'CircelCanvas',
onStart,
onEnd,
onStop,
config = {
width: '300',
heigth: '300',
size: 130, // 绘制圆形的最大尺寸,宽=高
borderWidth: 4, // 边框宽度
borderColor: "#fff", // 边框颜色
outerColor: "#fff", // 最外层底圆颜色
scheduleColor: "#fff", // 进度条动画颜色
fontColor: "#fff", // 字体颜色
ringColor: "#ffc720", // 进度条环形颜色
innerColor: "#4e84e5", // 最内圆底色
fontSize: 10, // 字号
time: 20, // 动画时长s
auto: false, // 自动开始
} }, ctd) => {
useEffect(() => {
init()
}, [config])
const init = () => {
const settings = {
id: id, // ID,canvas一定要有ID属性
size: getTruthPx(config.size), // 绘制圆形的最大尺寸,宽=高
borderWidth: getTruthPx(config.borderWidth), // 边框宽度
borderColor: config.borderColor, // 边框颜色
outerColor: config.outerColor, // 最外层底圆颜色
scheduleColor: config.scheduleColor, // 进度条动画颜色
fontColor: config.fontColor, // 字体颜色
ringColor: config.ringColor, // 进度条环形颜色
innerColor: config.innerColor,// 最内圆底色
fontSize: getTruthPx(config.fontSize),
time: config.time,
auto: config.auto, // 自动开始
}
ctd.current = new CircelCountdown(settings);
ctd.current.on(CD_EVENTS_TYPE.START, onStart)
ctd.current.on(CD_EVENTS_TYPE.END, onEnd)
ctd.current.on(CD_EVENTS_TYPE.STOP, onStop)
ctd.current.init();
console.log(ctd.current.eventsMap)
}
return <div className={"circel-wrp " + className}>
<canvas className="circel-con" id={id}></canvas>
</div>
})
export default observer(Circel)
\ No newline at end of file
.circel-wrp {
width: 100%;
height: 100%;
.circel-con {
width: 100%;
height: 100%;
}
}
import { SvgaPlayer } from "@spark/svgaplayer";
import API from "@src/api";
import store from "@src/store";
import modalStore from "@src/store/modal";
import { observer } from "mobx-react";
import React, { useEffect } from "react";
import { RES_PATH } from "../../../sparkrc";
import './index.less'
const OpenBox = () => {
const { boxState } = store
let drawInfo = null
useEffect(() => {
drawPrize()
}, [])
/** 进入抽奖 */
const drawPrize = async () => {
const res = await API.drawPrize({ drawType: boxState ? '1' : '2' })
if (res && res.success) {
drawInfo = res.data
} else {
// 接口报错关闭弹窗
setTimeout(() => {
store.getHomeData()
modalStore.closePop('OpenBox')
}, 1000);
}
}
/** 动画播放中 */
const onProcess = async (currentFrame) => {
// 抽奖请求结束关闭弹窗
if (currentFrame >= 70 && drawInfo) {
modalStore.closePop('OpenBox')
modalStore.pushPop('Getprize', drawInfo)
}
}
return <div className="openbox">
{boxState ?
<SvgaPlayer className="bigbox-svga" onProcess={onProcess} src={RES_PATH + 'svga/bigbox.svga'} /> :
<SvgaPlayer className="smailbox-svga" onProcess={onProcess} src={RES_PATH + 'svga/smailbox.svga'} />}
</div>
}
export default observer(OpenBox)
\ No newline at end of file
@import "../../res.less";
.bigbox-svga{
width: 750px;
height: 948px;
position: absolute;
.col-center();
}
.smailbox-svga{
width: 750px;
height: 808px;
position: absolute;
.col-center();
}
\ No newline at end of file
...@@ -8,6 +8,7 @@ import { observer } from 'mobx-react'; ...@@ -8,6 +8,7 @@ import { observer } from 'mobx-react';
import './homeDemo.less'; import './homeDemo.less';
import store from '@src/store'; import store from '@src/store';
import { all_page } from '@src/utils/constants'; import { all_page } from '@src/utils/constants';
import Circel from '@src/components/Circel';
...@@ -15,6 +16,7 @@ import { all_page } from '@src/utils/constants'; ...@@ -15,6 +16,7 @@ import { all_page } from '@src/utils/constants';
class HomeDemo extends React.Component { class HomeDemo extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.ctx = React.createRef()
} }
toPage = (page) => { toPage = (page) => {
...@@ -22,14 +24,42 @@ class HomeDemo extends React.Component { ...@@ -22,14 +24,42 @@ class HomeDemo extends React.Component {
} }
render() { render() {
const config = {
size: 300, // 绘制圆形的最大尺寸,宽=高
borderWidth: 1, // 边框宽度
borderColor: "#fff023", // 边框颜色
outerColor: "#fff", // 最外层底圆颜色
scheduleColor: "#fff023", // 进度条动画颜色
fontColor: "#000", // 字体颜色
ringColor: "#ffc720", // 进度条环形颜色
innerColor: "", // 最内圆底色
fontSize: 30, // 字号
time: 15, // 动画时长s
auto: true, // 自动开始
}
return ( return (
<div className="homeDemo"> <div className="homeDemo">
{all_page.map(({name, page}) => {
console.log(name,page) {all_page.map(({ name, page }) => {
console.log(name, page)
return <div key={name + page}> return <div key={name + page}>
<div onClick={() => this.toPage(page)}>{name}</div> <p onClick={() => this.toPage(page)}>{name}</p>
</div> </div>
})} })}
<div>
<p>环形进度条:</p>
<div className='test-btn-wrp'>
<div className='start-btn' onClick={() => this.ctx.current.start()}>开始</div>
<div className='stop-btn' onClick={() => this.ctx.current.stop()}>暂停</div>
</div>
<br />
<Circel ref={this.ctx}
className='Circel'
config={config}
onStart={() => console.log('开始')}
onStop={() => console.log('停止')}
onEnd={() => console.log('结束')} />
</div>
</div> </div>
); );
} }
......
@import "../../res.less"; @import "../../res.less";
.homeDemo {} .homeDemo {
.test-btn-wrp{
position: absolute;
width: 200px;
display: flex;
justify-content: space-between;
left: 150px;
color: red;
}
& > div {
p {
font-size: 43px;
}
margin-top: 10px;
}
.Circel{
width: 300px;
height: 300px;
left: 100px;
position: absolute;
}
}
...@@ -149,3 +149,16 @@ ...@@ -149,3 +149,16 @@
} }
} }
} }
// 淡出
.gradient() {
animation: gradient 0.5s forwards;
@keyframes gradient {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment