UNPKG

@ahmiao666/lucky-wheel-react

Version:
12 lines (11 loc) 31.3 kB
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).LuckyWheelReact={},t.React)}(this,function(t,e){"use strict";class i{constructor(t,e){this.rotateDeg=0,this.speed=i.DEFAULTS.SPEED,this.accelerationTime=i.DEFAULTS.ACCELERATION_TIME,this.decelerationTime=i.DEFAULTS.DECELERATION_TIME,this.stopOffsetRange=i.DEFAULTS.STOP_OFFSET_RANGE,this.targetDirection=i.DEFAULTS.TARGET_DIRECTION,this.startTime=0,this.endTime=0,this.stopDeg=0,this.endDeg=0,this.prizeDeg=0,this.FPS=i.DEFAULTS.FPS,this.images={arrow:void 0,background:void 0,centerButtonBackground:void 0,centerButtonArrow:void 0,sectors:new Map,sectorContents:new Map},this.imageLoadStates={arrow:!1,background:!1,centerButtonBackground:!1,centerButtonArrow:!1,sectors:new Map,sectorContents:new Map},this.step=0,this.centerButtonAnimationTime=0,this.imageLoaders=[{name:"background",getSrc:()=>this.config.backgroundImage,onLoad:t=>{this.images.background=t},onError:t=>console.warn("背景图片加载失败:",t),setLoaded:t=>{this.imageLoadStates.background=t}},{name:"arrow",getSrc:()=>this.config.arrow?.image,onLoad:t=>{this.images.arrow=t},onError:t=>console.warn("箭头图片加载失败:",t),setLoaded:t=>{this.imageLoadStates.arrow=t}},{name:"centerButtonBackground",getSrc:()=>this.config.centerButton?.backgroundImage,onLoad:t=>{this.images.centerButtonBackground=t},onError:t=>console.warn("中心按钮背景图片加载失败:",t),setLoaded:t=>{this.imageLoadStates.centerButtonBackground=t}},{name:"centerButtonArrow",getSrc:()=>this.config.centerButton?.arrow?.image,onLoad:t=>{this.images.centerButtonArrow=t},onError:t=>console.warn("中心按钮箭头图片加载失败:",t),setLoaded:t=>{this.imageLoadStates.centerButtonArrow=t}}],this.loadArrowImage=()=>this.reloadImageByName("arrow"),this.loadCenterButtonBackgroundImage=()=>this.reloadImageByName("centerButtonBackground"),this.loadCenterButtonArrowImage=()=>this.reloadImageByName("centerButtonArrow"),this.setStopOffsetRange=t=>{this.stopOffsetRange=Math.max(0,Math.min(1,t))},this.setTargetDirection=t=>{this.targetDirection=(t%360+360)%360,this.draw()},this.setArrowConfig=this.createConfigSetter("arrow",void 0,()=>{void 0!==this.config.arrow?.image&&(this.imageLoadStates.arrow=!1,this.loadArrowImage())}),this.setArrowDistance=t=>this.setArrowConfig({distance:Math.max(0,t)}),this.setArrowVisible=t=>this.setArrowConfig({visible:t}),this.setBorderConfig=this.createConfigSetter("border"),this.setBorderWidth=t=>this.setBorderConfig({width:Math.max(0,t)}),this.setBorderColor=t=>this.setBorderConfig({color:t}),this.setBorderStyle=t=>this.setBorderConfig({style:t}),this.setInnerPadding=t=>{this.config.innerPadding=Math.max(0,t),this.draw()},this.setSectorStrokeConfig=this.createConfigSetter("sectorStroke"),this.setSectorStrokeWidth=t=>this.setSectorStrokeConfig({width:Math.max(0,t)}),this.setSectorStrokeColor=t=>this.setSectorStrokeConfig({color:t}),this.setCenterButtonConfig=this.createConfigSetter("centerButton",void 0,()=>{void 0!==this.config.centerButton?.backgroundImage&&(this.imageLoadStates.centerButtonBackground=!1,this.loadCenterButtonBackgroundImage()),void 0!==this.config.centerButton?.arrow?.image&&(this.imageLoadStates.centerButtonArrow=!1,this.loadCenterButtonArrowImage())}),this.setCenterButtonAnimation=t=>this.setCenterButtonConfig({animation:t}),this.setCenterButtonAnimationConfig=t=>this.setCenterButtonConfig({animation:t}),this.setCenterButtonAnimationSpeed=t=>{const e=this.getAnimationConfig();this.setCenterButtonAnimationConfig({enabled:e.enabled,speed:Math.max(0,t),scale:e.scale})},this.setCenterButtonAnimationScale=t=>{const e=this.getAnimationConfig();this.setCenterButtonAnimationConfig({enabled:e.enabled,speed:e.speed,scale:Math.max(0,Math.min(1,t))})},this.setSectorTextConfig=this.createSectorConfigSetter("textConfig"),this.setBatchSectorTextConfig=t=>{t.forEach(({sectorId:t,config:e})=>this.setSectorTextConfig(t,e))},this.getSectorTextConfigById=t=>this.findSector(t)?.textConfig,this.resetSectorTextConfig=t=>{const e=this.findSector(t);e&&(e.textConfig=void 0,this.draw())},this.setSectorTextFontSize=(t,e)=>this.setSectorTextConfig(t,{fontSize:Math.max(1,e)}),this.setSectorTextColor=(t,e)=>this.setSectorTextConfig(t,{color:e}),this.setSectorTextLineHeight=(t,e)=>this.setSectorTextConfig(t,{lineHeight:Math.max(1,e)}),this.setSectorTextMaxLines=(t,e)=>this.setSectorTextConfig(t,{maxLines:Math.max(1,e)}),this.setSectorTextDirection=(t,e)=>this.setSectorTextConfig(t,{direction:e}),this.setSectorTextOffset=(t,e,i)=>this.setSectorTextConfig(t,{offsetX:Math.max(-1,Math.min(1,e)),offsetY:Math.max(-1,Math.min(1,i))}),this.setSectorTextAlign=(t,e)=>this.setSectorTextConfig(t,{textAlign:e}),this.setSectorTextFont=(t,e,i)=>{const s={fontFamily:e};i&&(s.fontWeight=i),this.setSectorTextConfig(t,s)},this.setSectorTextRadius=(t,e)=>this.setSectorTextConfig(t,{textRadius:Math.max(.1,Math.min(1,e))}),this.setSectorContentImageConfig=(t,e)=>{const i=this.findSector(t);if(i){if(!i.contentImage&&e.url)i.contentImage={url:e.url,...e};else{if(!i.contentImage)return void console.warn(`扇形 ${t} 需要先设置图片URL`);Object.assign(i.contentImage,e)}e.url&&(this.imageLoadStates.sectorContents.set(t,!1),this.loadSectorContentImages()),this.draw()}},this.setSectorContentImage=(t,e,i)=>this.setSectorContentImageConfig(t,{url:e,...i}),this.removeSectorContentImage=t=>{const e=this.findSector(t);e&&(e.contentImage=void 0,this.images.sectorContents.delete(t),this.imageLoadStates.sectorContents.set(t,!0),this.draw())},this.setSectorContentImageSize=(t,e,i)=>this.setSectorContentImageConfig(t,{width:Math.max(1,e),height:Math.max(1,i)}),this.setSectorContentImageOffset=(t,e,i)=>this.setSectorContentImageConfig(t,{offsetX:Math.max(-1,Math.min(1,e)),offsetY:Math.max(-1,Math.min(1,i))}),this.setSectorContentImageRadius=(t,e)=>this.setSectorContentImageConfig(t,{imageRadius:Math.max(.1,Math.min(1,e))}),this.setSectorContentImageRotation=(t,e)=>this.setSectorContentImageConfig(t,{rotation:e}),this.setSectorContentImageOpacity=(t,e)=>this.setSectorContentImageConfig(t,{opacity:Math.max(0,Math.min(1,e))}),this.setSectorContentImageVisible=(t,e)=>this.setSectorContentImageConfig(t,{visible:e}),this.setBackgroundImage=t=>{this.config.backgroundImage=t,this.imageLoadStates.background=!1,this.reloadImageByName("background")},this.canvas=t,this.ctx=t.getContext("2d"),this.config=e,this.devicePixelRatio=window.devicePixelRatio||1,this.config.size||(this.config.size=this.getDefaultSize()),this.setupHighDPICanvas(this.config.size),this.stopOffsetRange=e.stopOffsetRange??i.DEFAULTS.STOP_OFFSET_RANGE,this.targetDirection=e.targetDirection??i.DEFAULTS.TARGET_DIRECTION,this.prizeDeg=360/e.sectors.length,this.setupCenterButtonMouseEvents(),this.loadAllImages(),this.draw(),this.startContinuousAnimation()}getDefaultSize(){const t=this.canvas.parentElement;if(!t)return 300;const e=t.getBoundingClientRect(),i=this.config.canvasPadding??0,s=e.width-2*i,o=e.height-2*i,n=Math.min(s,o);return Math.max(n,100)}setupHighDPICanvas(t){const e=t+2*(this.config.canvasPadding??0);this.canvas.style.width=e+"px",this.canvas.style.height=e+"px",this.canvas.width=Math.floor(e*this.devicePixelRatio),this.canvas.height=Math.floor(e*this.devicePixelRatio),this.ctx.scale(this.devicePixelRatio,this.devicePixelRatio),this.ctx.imageSmoothingEnabled=!0,this.ctx.imageSmoothingQuality="high"}setupCenterButtonMouseEvents(){this.canvas.addEventListener("mousemove",t=>{this.isPointInCenterButton(t)?this.canvas.style.cursor="pointer":this.canvas.style.cursor="default"}),this.canvas.addEventListener("mouseleave",()=>{this.canvas.style.cursor="default"}),this.canvas.addEventListener("click",t=>{this.isPointInCenterButton(t)&&(this.onCenterButtonClickCallback?this.onCenterButtonClickCallback():0===this.step&&this.start())})}isPointInCenterButton(t){const e=this.config.centerButton;if(!e||!1===e.visible)return!1;const i=this.canvas.getBoundingClientRect(),s=this.config.canvasPadding??0,o=this.config.size+2*s,n=t.clientX-i.left-o/2,r=t.clientY-i.top-o/2,a=n/((e.width??20)/2),c=r/((e.height??20)/2);return a*a+c*c<=1}loadAllImages(){this.imageLoaders.forEach(t=>this.loadSingleImage(t)),this.loadSectorImages(),this.loadSectorContentImages()}loadSingleImage(t){const e=t.getSrc();t.setLoaded(!e),this.loadImage(e,e=>{t.onLoad(e),t.setLoaded(!0),this.draw()},e=>{t.onError(e),t.setLoaded(!0),this.draw()})}loadImage(t,e,i){if(!t)return;if(t instanceof HTMLImageElement)return void e(t);const s=new Image;s.onload=()=>e(s),s.onerror=()=>{i?.("string"==typeof t?t:"Unknown image source")},s.src=t}loadMapImages(t,e,i,s,o,n){t().forEach(t=>{const r=e(t),a=i(t);r?(o.set(a,!1),this.loadImage(r,t=>{s.set(a,t),o.set(a,!0),this.draw()},t=>{console.warn(`${n}[${a}]:`,t),o.set(a,!0),this.draw()})):o.set(a,!0)})}loadSectorImages(){this.loadMapImages(()=>this.config.sectors,t=>t.image?.url,t=>t.id,this.images.sectors,this.imageLoadStates.sectors,"扇形图片加载失败")}loadSectorContentImages(){this.loadMapImages(()=>this.config.sectors,t=>t.contentImage?.url,t=>t.id,this.images.sectorContents,this.imageLoadStates.sectorContents,"扇形内容图片加载失败")}reloadImageByName(t){const e=this.imageLoaders.find(e=>e.name===t);e&&this.loadSingleImage(e)}getAnimationConfig(){const t=this.config.centerButton;if(!t)return{enabled:!1,speed:i.DEFAULTS.ANIMATION_SPEED,scale:.1};const e=t.animation;return!1===e?{enabled:!1,speed:i.DEFAULTS.ANIMATION_SPEED,scale:.1}:!0===e||void 0===e?{enabled:!0,speed:i.DEFAULTS.ANIMATION_SPEED,scale:.1}:{enabled:!1!==e.enabled,speed:e.speed??i.DEFAULTS.ANIMATION_SPEED,scale:e.scale??.1}}startContinuousAnimation(){const t=()=>{const e=this.getAnimationConfig();this.centerButtonAnimationTime+=16.6*e.speed;const i=this.config.centerButton;!1!==i?.visible&&e.enabled&&this.draw(),this.continuousAnimationId=requestAnimationFrame(t)};t()}stopContinuousAnimation(){this.continuousAnimationId&&(cancelAnimationFrame(this.continuousAnimationId),this.continuousAnimationId=void 0)}start(){0===this.step&&(this.startTime=Date.now(),this.prizeFlag=void 0,this.step=1,this.run())}stop(t,e){if(0===this.step||3===this.step)return;this.onStopCallback=e,t=t||this.getRandomSectorId();const i=this.config.sectors.findIndex(e=>e.id===t);if(-1===i)return this.step=0,void(this.prizeFlag=-1);this.step=2,this.prizeFlag=i}setCenterButtonClickCallback(t){this.onCenterButtonClickCallback=t}updateConfig(t,e,i){const s=t.split(".");let o=this.config;for(let t=0;t<s.length-1;t++)o[s[t]]||(o[s[t]]={}),o=o[s[t]];const n=s[s.length-1];o[n]={...o[n],...e},i?.(),this.draw()}createConfigSetter(t,e,i){return s=>{e&&"object"==typeof s&&Object.keys(s).forEach(t=>{const i=s[t];void 0!==i&&(s[t]=e(i))}),this.updateConfig(t,s,i)}}findSector(t){const e=this.config.sectors.find(e=>e.id===t);return e||console.warn(`找不到ID为 ${t} 的扇形`),e}createSectorConfigSetter(t,e){return(i,s)=>{const o=this.findSector(i);if(!o)return;const n=o[t]||{},r=e&&"object"==typeof s?Object.fromEntries(Object.entries(s).map(([t,i])=>[t,e(i)])):s;o[t]={...n,...r},this.draw()}}run(t=0){if(0===this.step){if(void 0!==this.prizeFlag&&this.prizeFlag>=0){const t=this.config.sectors[this.prizeFlag];this.onStopCallback?.(t)}return}if(-1===this.prizeFlag)return;3!==this.step||this.endDeg||this.carveOnGunwaleOfAMovingBoat();const e=Date.now()-this.startTime,i=Date.now()-this.endTime;let s=this.rotateDeg;if(1===this.step||e<this.accelerationTime){this.FPS=e/t;const i=this.quadEaseIn(e,0,this.speed,this.accelerationTime);i===this.speed&&(this.step=2),s+=i%360}else 2===this.step?(s+=this.speed%360,void 0!==this.prizeFlag&&this.prizeFlag>=0&&(this.step=3,this.stopDeg=0,this.endDeg=0)):3===this.step&&(s=this.quadEaseOut(i,this.stopDeg,this.endDeg,this.decelerationTime),i>=this.decelerationTime&&(this.step=0));this.rotateDeg=s,this.draw(),this.animationId=requestAnimationFrame(()=>this.run(t+1))}carveOnGunwaleOfAMovingBoat(){const{prizeFlag:t,prizeDeg:e,rotateDeg:i}=this;this.endTime=Date.now();const s=this.stopDeg=i,o=this.speed,n=(2*Math.random()-1)*this.stopOffsetRange*e*.8/2;let r=0,a=0,c=0;for(;++r;){const h=360*r-t*e-i+n-e/2;let l=this.quadEaseOut(this.FPS,s,h,this.decelerationTime)-s;if(l>o){this.endDeg=o-a>l-o?h:c;break}c=h,a=l}}quadEaseIn(t,e,i,s){return i*(t/=s)*t+e}quadEaseOut(t,e,i,s){return-i*(t/=s)*(t-2)+e}draw(){const t=this.config.size,e=t+2*(this.config.canvasPadding??0),i=e/2,s=e/2,o=t/2-(this.config.border?.width??0)/2,n=o-(this.config.innerPadding??0);this.ctx.clearRect(0,0,e,e),this.ctx.save(),this.ctx.translate(i,s),this.drawBackgroundImage(o);const r=this.rotateDeg-90+this.prizeDeg/2+this.targetDirection;this.ctx.rotate(r*Math.PI/180);const a=2*Math.PI/this.config.sectors.length;this.config.sectors.forEach((t,e)=>{const i=e*a-a/2,s=i+a;this.drawSector(t,i,s,n)}),this.drawSectorDividers(a,n),this.ctx.restore(),this.drawWheelBorder(i,s,o),this.drawArrow(i,s),this.drawCenterButton(i,s)}drawBackgroundImage(t){if(!this.images.background||!this.imageLoadStates.background)return;this.ctx.save(),this.ctx.beginPath(),this.ctx.arc(0,0,t,0,2*Math.PI),this.ctx.clip();const e=2*t;this.ctx.drawImage(this.images.background,-t,-t,e,e),this.ctx.restore()}drawSector(t,e,i,s){this.ctx.beginPath(),this.ctx.arc(0,0,s,e,i),this.ctx.lineTo(0,0),this.ctx.fillStyle=t.color,this.ctx.fill(),this.drawSectorImage(t,e,i,s),this.drawSectorContentImage(t,e,i,s),this.drawSectorText(t,e,i,s)}getDefaultTextConfig(){return{fontSize:14,color:"#fff",lineHeight:16,maxLines:2,direction:"horizontal",offsetX:0,offsetY:0,textAlign:"center",fontFamily:"Arial",fontWeight:"normal",textRadius:.65}}getSectorTextConfig(t){return{...this.getDefaultTextConfig(),...t.textConfig}}drawSectorText(t,e,i,s){if(!t.text)return;const o=this.getSectorTextConfig(t),n=i-e,r=e+n/2;this.ctx.save(),this.ctx.fillStyle=o.color,this.ctx.font=`${o.fontWeight} ${o.fontSize}px ${o.fontFamily}`,this.ctx.textBaseline="middle";const a=s*o.textRadius,c=Math.cos(r)*a,h=Math.sin(r)*a;this.ctx.translate(c,h),"horizontal"===o.direction?this.ctx.rotate(r+Math.PI/2):this.ctx.rotate(r);const l=2*a*Math.sin(n/2),g=Math.max(o.fontSize/4,2),d=l/2-g,f=s-a-g,u=a-g,m=.8*Math.min(f,u);let S,x;"horizontal"===o.direction?(S=o.offsetX*d,x=o.offsetY*m):(S=o.offsetY*d,x=o.offsetX*m),this.ctx.translate(S,x);const{maxTextWidth:C,maxTextHeight:p}=this.calculateTextArea(n,a,o.direction);this.createTextClipPath(C,p,o.textAlign);const T=this.wrapTextWithConfig(t.text,C,o);this.renderTextLines(T,o,C,p),this.ctx.restore()}calculateTextArea(t,e,i){if("horizontal"===i){return{maxTextWidth:2*e*Math.sin(t/2)*.95,maxTextHeight:.7*e}}return{maxTextWidth:.8*e,maxTextHeight:2*e*Math.sin(t/2)*.9}}createTextClipPath(t,e,i){this.ctx.beginPath(),this.ctx.rect(-t/2,-e/2,t,e),this.ctx.clip()}wrapTextWithConfig(t,e,i){const s=[];let o="";const n=Array.from(t);for(let t=0;t<n.length;t++){const r=n[t],a=o+r;if(this.ctx.measureText(a).width>e&&""!==o){if(s.push(o.trim()),o=r,s.length>=i.maxLines-1){const i=o+n.slice(t+1).join("");if(this.ctx.measureText(i).width>e){let i=o;for(let s=t+1;s<n.length;s++){const t=n[s],o=i+t;if(this.ctx.measureText(o+"...").width>e)break;i+=t}for(;this.ctx.measureText(i+"...").width>e&&i.length>0;)i=i.slice(0,-1);o=i+"..."}else o=i;s.push(o.trim());break}}else o=a}return o.trim()&&s.length<i.maxLines&&s.push(o.trim()),s}renderTextLines(t,e,i,s){const o=-(t.length*e.lineHeight)/2+e.lineHeight/2;t.forEach((t,s)=>{const n=o+s*e.lineHeight;let r=0;"left"===e.textAlign?(this.ctx.textAlign="left",r=-i/2):"right"===e.textAlign?(this.ctx.textAlign="right",r=i/2):(this.ctx.textAlign="center",r=0),this.ctx.fillText(t,r,n)})}drawSectorDividers(t,e){const i=this.config.sectorStroke;if(i&&(i.width??0)>0){const s=this.config.sectors.length;this.ctx.strokeStyle=i.color??"#fff",this.ctx.lineWidth=i.width??2,this.ctx.setLineDash([]),this.ctx.lineCap="round",this.ctx.lineJoin="round";for(let i=0;i<s;i++){const s=(i+1)*t-t/2;this.ctx.beginPath(),this.ctx.moveTo(0,0),this.ctx.lineTo(Math.cos(s)*e,Math.sin(s)*e),this.ctx.stroke()}}}drawSectorImage(t,e,i,s){const o=this.images.sectors.get(t.id),n=this.imageLoadStates.sectors.get(t.id);if(!o||!n||!t.image)return;let r,a;if(this.ctx.save(),this.ctx.beginPath(),this.ctx.arc(0,0,s,e,i),this.ctx.lineTo(0,0),this.ctx.closePath(),this.ctx.clip(),void 0!==t.image.width&&void 0!==t.image.height)r=t.image.width,a=t.image.height;else{const t=i-e;r=2*s*Math.sin(t/2),a=s}const c=.5*s,h=e+(i-e)/2,l=Math.cos(h)*c,g=Math.sin(h)*c;this.ctx.translate(l,g),this.ctx.rotate(h+Math.PI/2),this.ctx.drawImage(o,-r/2,-a/2,r,a),this.ctx.restore()}drawSectorContentImage(t,e,i,s){if(!t.contentImage)return;const o=this.images.sectorContents.get(t.id),n=this.imageLoadStates.sectorContents.get(t.id);if(!o||!n)return;if(!1===t.contentImage.visible)return;this.ctx.save();const r=this.getDefaultContentImageConfig(t.contentImage),a=e+(i-e)/2,c=s*r.imageRadius,h=Math.cos(a)*c,l=Math.sin(a)*c,g=.3*s,d=-Math.sin(a),f=Math.cos(a),u=Math.cos(a),m=Math.sin(a),S=r.offsetX*g,x=r.offsetY*g,C=h+d*S+u*x,p=l+f*S+m*x;this.ctx.translate(C,p),this.ctx.rotate(a+r.rotation*Math.PI/180),this.ctx.globalAlpha=r.opacity,this.ctx.drawImage(o,-r.width/2,-r.height/2,r.width,r.height),this.ctx.restore()}getDefaultContentImageConfig(t){return{url:t.url,width:t.width??30,height:t.height??30,offsetX:t.offsetX??0,offsetY:t.offsetY??0,imageRadius:t.imageRadius??.5,rotation:t.rotation??0,opacity:t.opacity??1,visible:t.visible??!0}}drawArrow(t,e){const i=this.config.arrow||{};if(!1===i.visible)return;if(!this.imageLoadStates.arrow)return;const s=this.config.size/2-20,o=i.distance??s;if(this.ctx.save(),this.ctx.translate(t,e),this.ctx.rotate(this.targetDirection*Math.PI/180),this.images.arrow&&i.image){const t=i.imageWidth??30,e=i.imageHeight??30,s=(i.size??15)/15,n=t*s,r=e*s;this.ctx.drawImage(this.images.arrow,-n/2,-o-r/2,n,r)}else{const t=i.size??15,e=i.color??"#333";this.ctx.beginPath(),this.ctx.moveTo(0,-o),this.ctx.lineTo(-t,-o-1.2*t),this.ctx.lineTo(t,-o-1.2*t),this.ctx.closePath(),this.ctx.fillStyle=e,this.ctx.fill(),this.ctx.strokeStyle="#fff",this.ctx.lineWidth=1,this.ctx.stroke()}this.ctx.restore()}drawCenterButton(t,e){const i=this.config.centerButton;if(!i||!1===i.visible)return;if(!this.imageLoadStates.centerButtonBackground||!this.imageLoadStates.centerButtonArrow)return;let s=i.width??20,o=i.height??20;const n=i.backgroundColor??"#fff";let r=1;const a=this.getAnimationConfig();if(a.enabled&&(r=1+Math.sin(this.centerButtonAnimationTime)*a.scale,s*=r,o*=r),this.ctx.save(),this.ctx.translate(t,e),this.ctx.beginPath(),s===o?this.ctx.arc(0,0,s/2,0,2*Math.PI):this.ctx.ellipse(0,0,s/2,o/2,0,0,2*Math.PI),this.ctx.fillStyle=n,this.ctx.fill(),i.border){const t=i.border.width??2,e=i.border.color??"#333",s=i.border.style??"solid";this.ctx.strokeStyle=e,this.ctx.lineWidth=t,this.ctx.lineCap="round",this.ctx.lineJoin="round","dashed"===s?this.ctx.setLineDash([3*t,2*t]):"dotted"===s?this.ctx.setLineDash([t,t]):this.ctx.setLineDash([]),this.ctx.stroke()}this.images.centerButtonBackground&&i.backgroundImage&&(this.ctx.save(),this.ctx.clip(),this.ctx.drawImage(this.images.centerButtonBackground,-s/2,-o/2,s,o),this.ctx.restore()),i.arrow&&this.drawCenterButtonArrow(i.arrow),this.ctx.restore()}drawCenterButtonArrow(t){if(!t)return;let e=t.size??10;const i=t.color??"#333",s=t.offsetY??0,o=this.getAnimationConfig();if(o.enabled){e*=1+Math.sin(this.centerButtonAnimationTime)*o.scale}if(this.ctx.save(),this.ctx.rotate(this.targetDirection*Math.PI/180),this.ctx.translate(0,s),this.images.centerButtonArrow&&t.image){const i=e/10,s=(t.imageWidth??20)*i,o=(t.imageHeight??20)*i;this.ctx.drawImage(this.images.centerButtonArrow,-s/2,-o/2,s,o)}else{this.ctx.beginPath();const t=e/2;this.ctx.moveTo(0,-t),this.ctx.lineTo(.6*-t,t),this.ctx.lineTo(.6*t,t),this.ctx.closePath(),this.ctx.fillStyle=i,this.ctx.fill(),this.ctx.strokeStyle="#fff",this.ctx.lineWidth=1,this.ctx.stroke()}this.ctx.restore()}drawWheelBorder(t,e,i){const s=this.config.border;if(!s)return;const o=s.width??2,n=s.color??"#333",r=s.style??"solid";this.ctx.save(),this.ctx.translate(t,e),this.ctx.beginPath(),this.ctx.arc(0,0,i,0,2*Math.PI),this.ctx.strokeStyle=n,this.ctx.lineWidth=o,this.ctx.lineCap="round",this.ctx.lineJoin="round","dashed"===r?this.ctx.setLineDash([3*o,2*o]):"dotted"===r?this.ctx.setLineDash([o,o]):this.ctx.setLineDash([]),this.ctx.stroke(),this.ctx.restore()}getRandomSectorId(){return this.config.sectors[Math.floor(Math.random()*this.config.sectors.length)].id}setSectorImage(t,e,i){const s=this.config.sectors.find(e=>e.id===t);s?(s.image=e?{url:e,width:i?.width,height:i?.height}:void 0,e?(this.imageLoadStates.sectors.set(t,!1),this.loadSectorImages()):(this.imageLoadStates.sectors.set(t,!0),this.images.sectors.delete(t),this.draw())):console.warn(`找不到ID为 ${t} 的扇形`)}destroy(){this.animationId&&cancelAnimationFrame(this.animationId),this.stopContinuousAnimation(),this.canvas.style.cursor="default"}}i.DEFAULTS={SPEED:20,ACCELERATION_TIME:2500,DECELERATION_TIME:2500,STOP_OFFSET_RANGE:.6,TARGET_DIRECTION:0,FPS:16.6,ANIMATION_SPEED:.001,MIN_SIZE:100,MAX_OFFSET_RATIO:.8,ARROW_DISTANCE_OFFSET:20,WHEEL_BORDER_OFFSET:20,CENTER_BUTTON:{DEFAULT_WIDTH:20,DEFAULT_HEIGHT:20,DEFAULT_COLOR:"#fff",DEFAULT_ARROW_SIZE:10,DEFAULT_ARROW_COLOR:"#333"},ARROW:{DEFAULT_SIZE:15,DEFAULT_COLOR:"#333",DEFAULT_IMAGE_WIDTH:30,DEFAULT_IMAGE_HEIGHT:30},SECTOR:{DEFAULT_STROKE_WIDTH:2,DEFAULT_STROKE_COLOR:"#fff",CONTENT_IMAGE:{DEFAULT_WIDTH:30,DEFAULT_HEIGHT:30,DEFAULT_RADIUS:.5,DEFAULT_OPACITY:1}}};var s,o={exports:{}},n={};var r,a={}; /** * @license React * react-jsx-runtime.development.js * * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */"production"===process.env.NODE_ENV?o.exports=function(){if(s)return n;s=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function i(e,i,s){var o=null;if(void 0!==s&&(o=""+s),void 0!==i.key&&(o=""+i.key),"key"in i)for(var n in s={},i)"key"!==n&&(s[n]=i[n]);else s=i;return i=s.ref,{$$typeof:t,type:e,key:o,ref:void 0!==i?i:null,props:s}}return n.Fragment=e,n.jsx=i,n.jsxs=i,n}():o.exports=(r||(r=1,"production"!==process.env.NODE_ENV&&function(){function t(e){if(null==e)return null;if("function"==typeof e)return e.$$typeof===b?null:e.displayName||e.name||null;if("string"==typeof e)return e;switch(e){case u:return"Fragment";case S:return"Profiler";case m:return"StrictMode";case T:return"Suspense";case I:return"SuspenseList";case A:return"Activity"}if("object"==typeof e)switch("number"==typeof e.tag&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),e.$$typeof){case f:return"Portal";case C:return(e.displayName||"Context")+".Provider";case x:return(e._context.displayName||"Context")+".Consumer";case p:var i=e.render;return(e=e.displayName)||(e=""!==(e=i.displayName||i.name||"")?"ForwardRef("+e+")":"ForwardRef"),e;case y:return null!==(i=e.displayName||null)?i:t(e.type)||"Memo";case w:i=e._payload,e=e._init;try{return t(e(i))}catch(t){}}return null}function i(t){return""+t}function s(t){try{i(t);var e=!1}catch(t){e=!0}if(e){var s=(e=console).error,o="function"==typeof Symbol&&Symbol.toStringTag&&t[Symbol.toStringTag]||t.constructor.name||"Object";return s.call(e,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",o),i(t)}}function o(e){if(e===u)return"<>";if("object"==typeof e&&null!==e&&e.$$typeof===w)return"<...>";try{var i=t(e);return i?"<"+i+">":"<...>"}catch(t){return"<...>"}}function n(){return Error("react-stack-top-frame")}function r(){var e=t(this.type);return D[e]||(D[e]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),void 0!==(e=this.props.ref)?e:null}function c(e,i,o,n,a,c,g,f){var u,m=i.children;if(void 0!==m)if(n)if(B(m)){for(n=0;n<m.length;n++)h(m[n]);Object.freeze&&Object.freeze(m)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else h(m);if(E.call(i,"key")){m=t(e);var S=Object.keys(i).filter(function(t){return"key"!==t});n=0<S.length?"{key: someKey, "+S.join(": ..., ")+": ...}":"{key: someKey}",O[m+n]||(S=0<S.length?"{"+S.join(": ..., ")+": ...}":"{}",console.error('A props object containing a "key" prop is being spread into JSX:\n let props = %s;\n <%s {...props} />\nReact keys must be passed directly to JSX without using spread:\n let props = %s;\n <%s key={someKey} {...props} />',n,m,S,m),O[m+n]=!0)}if(m=null,void 0!==o&&(s(o),m=""+o),function(t){if(E.call(t,"key")){var e=Object.getOwnPropertyDescriptor(t,"key").get;if(e&&e.isReactWarning)return!1}return void 0!==t.key}(i)&&(s(i.key),m=""+i.key),"key"in i)for(var x in o={},i)"key"!==x&&(o[x]=i[x]);else o=i;return m&&function(t,e){function i(){l||(l=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",e))}i.isReactWarning=!0,Object.defineProperty(t,"key",{get:i,configurable:!0})}(o,"function"==typeof e?e.displayName||e.name||"Unknown":e),function(t,e,i,s,o,n,a,c){return i=n.ref,t={$$typeof:d,type:t,key:e,props:n,_owner:o},null!==(void 0!==i?i:null)?Object.defineProperty(t,"ref",{enumerable:!1,get:r}):Object.defineProperty(t,"ref",{enumerable:!1,value:null}),t._store={},Object.defineProperty(t._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(t,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(t,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:a}),Object.defineProperty(t,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:c}),Object.freeze&&(Object.freeze(t.props),Object.freeze(t)),t}(e,m,c,0,null===(u=v.A)?null:u.getOwner(),o,g,f)}function h(t){"object"==typeof t&&null!==t&&t.$$typeof===d&&t._store&&(t._store.validated=1)}var l,g=e,d=Symbol.for("react.transitional.element"),f=Symbol.for("react.portal"),u=Symbol.for("react.fragment"),m=Symbol.for("react.strict_mode"),S=Symbol.for("react.profiler"),x=Symbol.for("react.consumer"),C=Symbol.for("react.context"),p=Symbol.for("react.forward_ref"),T=Symbol.for("react.suspense"),I=Symbol.for("react.suspense_list"),y=Symbol.for("react.memo"),w=Symbol.for("react.lazy"),A=Symbol.for("react.activity"),b=Symbol.for("react.client.reference"),v=g.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,E=Object.prototype.hasOwnProperty,B=Array.isArray,k=console.createTask?console.createTask:function(){return null},D={},L=(g={"react-stack-bottom-frame":function(t){return t()}})["react-stack-bottom-frame"].bind(g,n)(),M=k(o(n)),O={};a.Fragment=u,a.jsx=function(t,e,i,s,n){var r=1e4>v.recentlyCreatedOwnerStacks++;return c(t,e,i,!1,0,n,r?Error("react-stack-top-frame"):L,r?k(o(t)):M)},a.jsxs=function(t,e,i,s,n){var r=1e4>v.recentlyCreatedOwnerStacks++;return c(t,e,i,!0,0,n,r?Error("react-stack-top-frame"):L,r?k(o(t)):M)}}()),a);var c=o.exports;const h=e.forwardRef(({config:t,className:s,style:o,onStop:n,onCenterButtonClick:r,onStart:a,onReady:h},l)=>{const g=e.useRef(null),d=e.useRef(null),f=e.useRef(!1);return e.useEffect(()=>{if(g.current&&!f.current){try{const e=new i(g.current,t);d.current=e,f.current=!0,r?e.setCenterButtonClickCallback(()=>{r()}):e.setCenterButtonClickCallback(()=>{a?.(),e.start()}),h?.(e)}catch(t){console.error("初始化幸运转盘失败:",t)}return()=>{d.current&&(d.current.destroy(),d.current=null,f.current=!1)}}},[]),e.useImperativeHandle(l,()=>({getEngine:()=>d.current,start:()=>{d.current&&d.current.start()},stop:(t,e)=>{d.current&&d.current.stop(t,t=>{e?.(t),n?.(t)})},setStopOffsetRange:t=>{d.current?.setStopOffsetRange(t)},setTargetDirection:t=>{d.current?.setTargetDirection(t)},setArrowConfig:t=>{d.current?.setArrowConfig(t)},setArrowDistance:t=>{d.current?.setArrowDistance(t)},setArrowVisible:t=>{d.current?.setArrowVisible(t)},setBorderConfig:t=>{d.current?.setBorderConfig(t)},setBorderWidth:t=>{d.current?.setBorderWidth(t)},setBorderColor:t=>{d.current?.setBorderColor(t)},setBorderStyle:t=>{d.current?.setBorderStyle(t)},setInnerPadding:t=>{d.current?.setInnerPadding(t)},setCenterButtonClickCallback:t=>{d.current?.setCenterButtonClickCallback(t)},setSectorStrokeConfig:t=>{d.current?.setSectorStrokeConfig(t)},setSectorStrokeWidth:t=>{d.current?.setSectorStrokeWidth(t)},setSectorStrokeColor:t=>{d.current?.setSectorStrokeColor(t)},setCenterButtonConfig:t=>{d.current?.setCenterButtonConfig(t)},setCenterButtonAnimation:t=>{d.current?.setCenterButtonAnimation(t)},setCenterButtonAnimationConfig:t=>{d.current?.setCenterButtonAnimationConfig(t)},setCenterButtonAnimationSpeed:t=>{d.current?.setCenterButtonAnimationSpeed(t)},setCenterButtonAnimationScale:t=>{d.current?.setCenterButtonAnimationScale(t)},setBackgroundImage:t=>{d.current?.setBackgroundImage(t)},setSectorImage:(t,e,i)=>{d.current?.setSectorImage(t,e,i)},setSectorTextConfig:(t,e)=>{d.current?.setSectorTextConfig(t,e)},setBatchSectorTextConfig:t=>{d.current?.setBatchSectorTextConfig(t)},getSectorTextConfigById:t=>d.current?.getSectorTextConfigById(t),resetSectorTextConfig:t=>{d.current?.resetSectorTextConfig(t)},setSectorTextFontSize:(t,e)=>{d.current?.setSectorTextFontSize(t,e)},setSectorTextColor:(t,e)=>{d.current?.setSectorTextColor(t,e)},setSectorTextLineHeight:(t,e)=>{d.current?.setSectorTextLineHeight(t,e)},setSectorTextMaxLines:(t,e)=>{d.current?.setSectorTextMaxLines(t,e)},setSectorTextDirection:(t,e)=>{d.current?.setSectorTextDirection(t,e)},setSectorTextOffset:(t,e,i)=>{d.current?.setSectorTextOffset(t,e,i)},setSectorTextAlign:(t,e)=>{d.current?.setSectorTextAlign(t,e)},setSectorTextFont:(t,e,i)=>{d.current?.setSectorTextFont(t,e,i)},setSectorTextRadius:(t,e)=>{d.current?.setSectorTextRadius(t,e)},setSectorContentImageConfig:(t,e)=>{d.current?.setSectorContentImageConfig(t,e)},setSectorContentImage:(t,e,i)=>{d.current?.setSectorContentImage(t,e,i)},removeSectorContentImage:t=>{d.current?.removeSectorContentImage(t)},setSectorContentImageSize:(t,e,i)=>{d.current?.setSectorContentImageSize(t,e,i)},setSectorContentImageOffset:(t,e,i)=>{d.current?.setSectorContentImageOffset(t,e,i)},setSectorContentImageRadius:(t,e)=>{d.current?.setSectorContentImageRadius(t,e)},setSectorContentImageRotation:(t,e)=>{d.current?.setSectorContentImageRotation(t,e)},setSectorContentImageOpacity:(t,e)=>{d.current?.setSectorContentImageOpacity(t,e)},setSectorContentImageVisible:(t,e)=>{d.current?.setSectorContentImageVisible(t,e)},destroy:()=>{d.current&&(d.current.destroy(),d.current=null)}})),c.jsx("canvas",{ref:g,className:s,style:o})});h.displayName="ReactLuckyWheel",t.LuckyWheelEngine=i,t.ReactLuckyWheel=h}); //# sourceMappingURL=index.umd.js.map