| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580 |
- import { IBestToast } from '@ibestservices/ibest-ui'
- import { CellStorage } from '.'
- import { YTAvoid } from '../../utils/YTAvoid'
- import { yTRouter } from '../../utils/YTRouter'
- import { yTToast } from '../../utils/YTToast'
- import { ReNameInput } from './ReNameInput'
- import { Cell, Sector } from './Sector'
- @Component
- export struct BigWheelView{
- @StorageProp(YTAvoid.SAFE_TOP_KEY) safeBottom: number = 0
- //持久化的数组,方便下次打开应用直接承接上一次编辑的数据
- @StorageLink('bigwheel')
- DBCells:CellStorage[]=[]
- //持久化没选中数组id,方便退出应用再次开发还能接着转
- @StorageLink('unselectbigwheel')
- DBUnselectCell:number[]=[]
- //持久化选中数组id,方便退出应用再次开发还能接着转
- @StorageLink('selectbigwheel')
- DBSelectCell:number[]=[]
- //转盘操作数组
- @State cells: Cell[] = []; // 存储单元格的数组
- //随着每次切换操作,需要重置触发画布重新画
- @Watch('drawCircleWithCustomRadii')
- @State changeCanvas:number=0
- @State wheelWidth: number = 250; // 转盘的宽度
- @State currentAngle: number = 0; // 当前转盘的角度
- @State selectedName: string = ""; // 选中的名称
- //持久化存储是否重复
- @StorageLink('isrepeat')
- isRepeat:boolean=false
- //需要操作的数组,选中没选中
- @State selected:Cell[]=[]
- @State UnSelected:Cell[]=[]
- @State randomAngle:number=0
- isAnimating: boolean = false; // 动画状态
- colorIndex: number = 0; // 颜色索引
- @StorageLink('duration')
- spinDuration:number=5000
- @State spinDurationTime:number=this.spinDuration/1000
- //是否显示设置界面
- @State isShow:boolean=false
- private settings: RenderingContextSettings = new RenderingContextSettings(true)
- private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
- //转盘选中判断
- @State currSelectNumber:number=1
- @State number:number=4 //转盘数量
- //打开半模态命名模态
- @State isshowRename:boolean=false
- /**
- * 后面迭代保障可以更改转盘的初始的颜色
- */
- colorPalette: string[] = [ // 颜色调色板
- "#fff",
- "#fff",
- "#fff",
- "#fff",
- "#fff",
- "#fff",
- ];
- filter(){
- //每次选中的数组有变化,那就要重新过滤,就要移除没选中的数组里面的数据
- //先获取选中id数组
- this.DBSelectCell=this.selected.map(item=>item.id) as number[]
- //再根据选中数组id过滤没选中数组
- this.UnSelected = this.cells.filter(item => !this.DBSelectCell.includes(item.id));
- //再根据没选中数组,获取没选中数组id
- this.DBUnselectCell=this.UnSelected.map(item=>item.id)
- }
- // 组件即将出现时调用
- aboutToAppear() {
- if(this.DBCells.length==0) {
- this.cells=[]
- this.cells.push(new Cell(1, 1, "转盘1",'#fff' ));
- this.cells.push(new Cell(2, 1, "转盘2", '#fff' ));
- this.cells.push(new Cell(3, 1, "转盘3",'#fff' ));
- this.cells.push(new Cell(4, 1, "转盘4", '#fff' ));
- this.calculateAngles(); // 计算角度
- //如果没有之前的记录,存一下没选中的数组和数组id
- this.UnSelected=this.cells
- this.DBUnselectCell=this.UnSelected.map(item=>item.id)
- }else{
- //如果有上次记录,需要先获取上一次得轮盘
- this.DBCells.forEach((item)=>{
- this.cells.push(new Cell(item.id,item.proportion,item.title,item.color))
- })
- this.calculateAngles(); // 计算角度
- //轮盘数
- this.number=this.cells.length
- //再获取没选中的和选中的
- this.selected=[]
- this.UnSelected=[]
- for (let index = 0; index < this.cells.length; index++) {
- //遍历扇形,通过选中的数组id,找到选中的数组
- this.DBSelectCell.forEach((I)=>{
- if(this.cells[index].id==I){
- this.selected.push(this.cells[index])
- }
- })
- }
- //再找没选中数组
- this.UnSelected=this.cells.filter((item)=>{
- return !this.DBSelectCell.includes(item.id)
- })
- this.currSelectNumber=this.number-3
- }
- }
- //动画
- private startAnimation(){
- if (this.isAnimating) { // 如果正在动画中,返回
- return;
- }
- if(this.selected.length==this.cells.length){
- yTToast.doubleConfirm({
- message: '当前已经转完所有,是否重置', click: () => {
- //背景色变成白色
- this.cells.forEach((item)=>{
- item.color='#fff'
- })
- //重置,所有的转盘重新开始
- this.selected=[]
- this.UnSelected=[]
- this.UnSelected=this.cells
- this.DBUnselectCell=this.UnSelected.map(item=>item.id)
- this.DBSelectCell=[]
- this.DBCells=this.cells.map((item)=>{
- return {
- id:item.id,
- title:item.title,
- proportion:item.proportion,
- color:item.color
- } as CellStorage
- })
- yTToast.hide()
- }
- })
- return
- }
- //如果都已经被选了,则不能再转了
- this.calculateNoSelectedAngles()
- this.selectedName = ""; // 清空选中的名称
- this.isAnimating = true; // 设置动画状态为正在动画
- animateTo({ // 开始动画
- duration: this.spinDuration, // 动画持续时间为5000毫秒
- curve: Curve.EaseInOut, // 动画曲线为缓入缓出
- onFinish: () => { // 动画完成后的回调
- this.currentAngle %= 360; // 保持当前角度在0到360之间
- for (const cell of this.cells) {
- // 遍历每个单元格
- // 检查当前角度是否在单元格的角度范围内
- if (360 - this.currentAngle >= cell.angleStart && 360 - this.currentAngle <= cell.angleEnd) {
- if(!this.isRepeat) {
- this.selected.push(cell)
- // this.DBSelectCell.push(cell)
- //这里需要下次点击的时候
- cell.color = '#e5e7ea'
- this.filter()
- }else{
- // this.selected=[]
- }
- this.selectedName = cell.title; // 设置选中的名称为当前单元格的标题
- //每次转弯要保存
- break; // 找到后退出循环
- }
- }
- this.DBCells=this.cells.map((item)=>{
- return {
- id:item.id,
- title:item.title,
- proportion:item.proportion,
- color:item.color
- } as CellStorage
- })
- // promptAction.showToast({
- // message:'选中的数组'+JSON.stringify(this.selected)+'没选中的数组'+JSON.stringify(this.UnSelected)+'选中的数组id'+JSON.stringify(this.DBSelectCell)+'没选中的数组id'+JSON.stringify(this.DBUnselectCell)
- // })
- this.isAnimating = false; // 设置动画状态为未动画
- },
- }, () => { // 动画进行中的回调
- //在这里判断
- // this.randomAngle=Math.floor(Math.random()*360)
- // this.currentAngle += (360 * this.spinDuration/1000 + Math.floor(Math.random() * 360)); // 更新当前角度,增加随机旋转
- //在这里算已经选过的,不能在指了
- // this.currentAngle += (360 * this.spinDuration/1000 + Math.floor(Math.random() * 360)); // 更新当前角度,增加随机旋转
- // promptAction.showToast({
- // message:this.randomAngle.toString()
- // })
- this.currentAngle += (360 * this.spinDuration/1000)+this.randomAngle
- });
- }
- // 计算每个单元格的角度
- private calculateAngles() {
- // 根据比例计算总比例,后续迭代需要计算不同比例的角度
- const totalProportion = this.cells.reduce((sum, cell) => sum + cell.proportion, 0);
- this.cells.forEach(cell => {
- cell.angle = (cell.proportion * 360) / totalProportion; // 计算每个单元格的角度
- });
- let cumulativeAngle = 0; // 累计角度
- this.cells.forEach(cell => {
- cell.angleStart = cumulativeAngle; // 设置起始角度
- cumulativeAngle += cell.angle; // 更新累计角度
- cell.angleEnd = cumulativeAngle; // 设置结束角度
- cell.rotate = cumulativeAngle - (cell.angle / 2); // 计算旋转角度
- });
- //手动触发画布,重新画
- this.changeCanvas++
- }
- //不允许重复时,需要在没选中的数组里面选,然后计算角度,使其转到没选中数组的区间当中去
- private calculateNoSelectedAngles(){
- if(this.UnSelected.length!=0) {
- //随机选取一个没有选中的角度范围扇形
- const currangle=this.currentAngle%360
- const randomIndex = Math.floor(Math.random() * this.UnSelected.length) as number
- const ranNumStart = 360-this.UnSelected[randomIndex].angleEnd
- const ranNumEnd = 360-this.UnSelected[randomIndex].angleStart
- this.randomAngle =Math.floor(Math.random() * (ranNumEnd - ranNumStart) + ranNumStart)-currangle
- }
- }
- //画线
- private drawCircleWithCustomRadii() {
- // this.context.clearRect(0,0,0,0)
- this.context.clearRect(0, 0, 250, 250); // 清空Canvas的内容
- const centerX = 125 // 圆心x坐标
- const centerY = 125 // 圆心y坐标
- const radius = 125 // 圆半径
- // 根据自定义角度数组绘制半径线
- this.cells.forEach(angle => {
- // 将角度转换为弧度(Canvas使用弧度制)
- const radians = (angle.angleEnd-90) * Math.PI / 180
- // 计算半径线终点坐标
- const endX = centerX + radius * Math.cos(radians)
- const endY = centerY + radius * Math.sin(radians)
- // 绘制半径线
- this.context.beginPath()
- this.context.moveTo(centerX, centerY)
- this.context.lineTo(endX, endY)
- this.context.strokeStyle = '#efd4f9' // 红色半径线
- this.context.lineWidth = 1.5
- this.context.stroke()
- })
- }
- build() {
- Stack({alignContent:Alignment.Center}){
- Stack({alignContent:Alignment.Top}){
- Column() {
- // YTHeader({ title: '大转盘', })
- Row(){
- Image($r('app.media.ic_back'))
- .width(24)
- .margin({ left: 16 })
- .onClick(()=>{
- yTRouter.routerBack()
- })
- Text('大转盘')
- .fontSize(18)
- .fontWeight(700)
- .fontColor(Color.Black)
- Image($r('app.media.Subtract'))
- .width(24)
- .margin({right: 16 })
- .onClick(()=>{
- this.isShow=true
- })
- }.width('100%')
- .height(84)
- .justifyContent(FlexAlign.SpaceBetween)
- .padding({ top: 44 })
- .margin({bottom:20})
- Row(){
- Column(){}.width(24).height(24)
- // Image($r('[basic].media.voicemuisc')).width(24)
- Text('重置').fontColor('rgba(0, 0, 0, 0.65)').onClick(()=>{
- //背景色变成白色
- this.cells.forEach((item)=>{
- item.color='#fff'
- })
- this.selected=[]
- this.UnSelected=[]
- this.UnSelected=this.cells
- this.DBUnselectCell=this.UnSelected.map(item=>item.id)
- this.DBSelectCell=[]
- this.DBCells=this.cells.map((item)=>{
- return {
- id:item.id,
- title:item.title,
- proportion:item.proportion,
- color:item.color
- } as CellStorage
- })
- })
- }.width('100%')
- .justifyContent(FlexAlign.SpaceBetween)
- .padding({left:30,right:30})
- // 显示当前状态
- Text(this.isAnimating ? '旋转中' : `${this.selectedName}`)
- .fontSize(20)
- .fontColor("#0b0e15")
- .height(40)
- .margin({top:100})
- Stack() {
- Stack() {
- // 遍历每个单元格并绘制扇形
- ForEach(this.cells, (cell: Cell) => {
- Stack() {
- Sector({ radius: lpx2px(this.wheelWidth) / 2, angle: cell.angle, color: cell.color }); // 创建扇形
- Text(cell.title).fontColor(Color.Black).fontWeight(700).margin({ bottom: this.wheelWidth / 1.4 }); // 显示单元格标题
- }.width('100%').height('100%').rotate({ angle: cell.rotate }); // 设置宽度和高度,并旋转
- });
- }
- .borderRadius('50%') // 设置圆角
- // .backgroundColor(Color.Gray) // 设置背景颜色
- .width(this.wheelWidth) // 设置转盘宽度
- .height(this.wheelWidth) // 设置转盘高度
- .rotate({ angle: this.currentAngle }); // 旋转转盘
- Column() {
- Canvas(this.context)
- .width(250)
- .height(250)
- .borderRadius('50%')
- .backgroundColor(Color.Transparent)
- .onReady(() => {
- this.drawCircleWithCustomRadii()
- })
- } .width(this.wheelWidth) // 设置转盘宽度
- .height(this.wheelWidth) // 设置转盘高度
- .justifyContent(FlexAlign.Center)
- .rotate({ angle: this.currentAngle }) // 旋转转盘
- Image($r('app.media.zhizheng'))
- .width(63) // 设置按钮宽度
- .height(79) // 设置按钮高度
- .objectFit(ImageFit.Contain)
- .clickEffect({ level: ClickEffectLevel.LIGHT }) // 设置点击效果
- }
- .width(this.wheelWidth+15)
- .height(this.wheelWidth+15)
- .backgroundImage($r('app.media.xuanzhuankuang'))
- .backgroundImageSize({width:'100%',height:'100%'})
- .backgroundImagePosition(Alignment.Center)
- Button('转一转').fontColor(Color.White)
- .backgroundColor('#fd54e3').width(246)
- .height(44).borderRadius(24)
- .margin({top:99,bottom:48})
- .onClick(()=>{
- this.startAnimation()
- })
- Row({space:15}) {
- ForEach([2,3,4,5],(item:number,index:number)=>{
- Text((item+1).toString())
- .width(40)
- .height(40)
- .textAlign(TextAlign.Center)
- .border({width:1,color:'#000000'})
- .borderRadius('50%')
- .backgroundColor(this.currSelectNumber==index?'#bff2ff':'#f2f2f2')
- .onClick(()=>{
- this.currSelectNumber=index
- this.number=item+1
- const arr=this.cells
- this.cells=[]
- //如果选中的长度比之前要长
- if(this.number>arr.length) {
- for (let i = 0; i < this.number; i++) {
- if (i < arr.length) {
- this.cells.push(arr[i])
- }else{
- this.cells.push(new Cell(i + 1, 1, "转盘" + (i + 1), '#fff'));
- }
- }
- }else{
- //短
- for (let i = 0; i < this.number; i++) {
- this.cells.push(arr[i])
- }
- }
- //要继承之前没选中的,选中的数组根据id
- this.calculateAngles(); // 重新计算角度
- //选中的和没选中的也要重新计算角度
- //找到id重新过滤
- this.selected=[]
- this.UnSelected=[]
- //每次选择扇形数量都要重新更新一下
- let a=[] as number[]
- let b=[] as number[]
- //先获取选中数组,没选中数组id
- a=this.DBSelectCell
- b=this.DBUnselectCell
- //遍历扇形,获取选中数组和没选中数组
- for (let i = 0; i < this.cells.length; i++) {
- for(let j=0;j<a.length;j++){
- if(a[j]==this.cells[i].id){
- this.selected.push(this.cells[i])
- continue
- }
- }
- }
- //找到选中的id数组
- this.DBSelectCell=this.selected.map(item=>item.id)
- this.UnSelected=this.cells.filter((item)=>{
- return !this.DBSelectCell.includes(item.id)
- })
- this.DBUnselectCell=this.UnSelected.map(item=>item.id)
- this.DBCells=[]
- this.DBCells=this.cells.map((item)=>{
- return {
- id:item.id,
- title:item.title,
- proportion:item.proportion,
- color:item.color
- } as CellStorage
- })
- })
- })
- Row(){
- Image($r('app.media.zidingyi')).width(16)
- Text('命名').fontSize(12).fontColor(Color.White)
- }.width(64)
- .height(40)
- .borderRadius(20)
- .backgroundColor('rgba(255, 157, 240, 1)')
- .justifyContent(FlexAlign.Center)
- .onClick(()=>{
- this.isshowRename=true
- })
- }
- }.width('100%').padding({ bottom: this.safeBottom })
- .justifyContent(FlexAlign.Center).onClick(()=>{
- this.isShow=false
- })
- //是否展示选项设置
- if(this.isShow) {
- this.BigWheelManagerBuilder()
- }
- }.height('100%')
- .backgroundImage($r('app.media.backimgNumber'))
- .backgroundImageSize({width:'100%',height:'100%'})
- //重命名设置
- if(this.isshowRename) {
- this.ReNameBuilder()
- }
- }
- }
- @Builder
- ReNameBuilder(){
- Column() {
- Column() {
- Row() {
- Text('转盘命名').fontSize(20).fontWeight(500).fontColor('#FF1C1C1C').margin({left:110,right:64,top:24})
- Column() {
- Image($r('app.media.quxiaocl')).width(10)
- }.width(24)
- .height(24)
- .backgroundColor(Color.White)
- .justifyContent(FlexAlign.Center)
- .borderRadius('50%')
- .onClick(() => {
- this.isshowRename = false
- })
- }.width('100%')
- Column({space:10}){
- ForEach(this.cells,(item:Cell,index:number)=>{
- ReNameInput({
- text:item.title,
- num:index+1,
- inputChange:(value:string)=>{
- item.title=value
- }
- })
- })
- }
- }.width(300).height(318)
- .justifyContent(FlexAlign.Start)
- .borderRadius(20)
- .padding({left:16,right:16})
- .linearGradient({
- angle:135,
- colors:[
- ['rgba(248, 211, 249, 1)',0.2],
- ['rgba(192, 242, 255, 1)',1]
- ]
- })
- }.width('100%').height('100%').justifyContent(FlexAlign.Center).backgroundColor('rgba(30, 30, 30,0.5)')
- }
- @Builder
- BigWheelManagerBuilder(){
- Column() {
- //允许结果是否重复
- Row() {
- Row({ space: 10 }) {
- Image($r('app.media.qiehuan')).width(24)
- Text('允许结果重复').fontWeight(700)
- }
- Row() {
- Toggle({ type: ToggleType.Switch ,isOn:$$this.isRepeat})
- .width(38)
- .height(20)
- .selectedColor('rgba(253, 84, 227, 1)') //打开状态下的背景颜色
- .switchStyle({
- pointRadius: 8, //圆形滑块半径
- trackBorderRadius: 14, //滑轨的圆角
- pointColor: Color.White, //圆形滑块颜色 switchPointColor不生效
- unselectedColor: 'rgba(233, 233, 234, 1)' //关闭状态的背景颜色
- })
- .onClick(() => {
- this.isRepeat=!this.isRepeat
- })
- }
- }
- .width('100%')
- .height(40)
- .backgroundColor(Color.White)
- .borderRadius(8)
- .justifyContent(FlexAlign.SpaceBetween)
- .padding({ left: 12, right: 12 })
- .alignItems(VerticalAlign.Center)
- Row() {
- Text('每次转动轮盘可能会随机选中相同的选项').fontSize(12).fontColor('rgba(0, 0, 0, 0.45)')
- }.width('100%')
- .justifyContent(FlexAlign.Start)
- .padding({ left: 22 })
- .margin({ bottom: 25, top: 10 })
- Row() {
- Row({ space: 10 }) {
- Image($r('app.media.xuanzhuantime')).width(24)
- Text('旋转时长').fontWeight(700)
- }
- Row() {
- Counter() {
- Text(this.spinDurationTime.toString() + 's').border({ width: 0 })
- }
- .onInc(() => {
- this.spinDurationTime++
- this.spinDuration = this.spinDurationTime * 1000
- })
- .onDec(() => {
- if(this.spinDurationTime==1){
- IBestToast.show({
- message:'秒数最低为1秒'
- })
- return
- }
- this.spinDurationTime--
- this.spinDuration = this.spinDurationTime * 1000
- })
- }
- }
- .width('100%')
- .height(40)
- .backgroundColor(Color.White)
- .borderRadius(8)
- .justifyContent(FlexAlign.SpaceBetween)
- .padding({ left: 12, right: 12 })
- .alignItems(VerticalAlign.Center)
- }
- .width('100%')
- .height(214)
- .padding({ left: 22, top: 56, right: 22 })
- .borderRadius({ bottomLeft: 20, bottomRight: 20 })
- .linearGradient({
- angle: 135,
- colors: [
- ['rgba(239, 144, 237, 1)', 0.2],
- ['rgba(191, 242, 255, 1)', 1]
- ]
- })
- }
- }
|