进入/退出全屏时，弹幕字体大小变化后才旋转屏幕
=======================

#### 问题现象

播放视频时，进入横向全屏前弹幕字体会突然变大，退出横向全屏前弹幕字体会突然变小。

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/5b/v3/kCBFcS-LQ6q1FWSoETMBGA/zh-cn_image_0000002658913731.png?HW-CC-KV=V1&HW-CC-Date=20260709T133910Z&HW-CC-Expire=86400&HW-CC-Sign=65E98F8DC1F21C30BA42B501DD3FBD80376F7FBF4DE9823EC1CE8C5820FA415B "点击放大")

#### 背景知识

*   [setPreferredOrientation](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-window-window#setpreferredorientation9)可设置主窗口的显示方向属性，使用callback异步回调。
*   基于[setInterval](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-timer#setinterval)函数和[translate](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-page-transition-animation#translate)属性可实现弹幕移动动画。
*   设置[onAreaChange](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-component-area-change-event#onareachange)，组件区域变化时触发该回调。仅会响应由布局变化所导致的组件大小、位置发生变化时的回调。

#### 问题定位

全局搜索setPreferredOrientation，查看进入和退出横向全屏时的设置，应用在更改屏幕方向时更改了弹幕的字号。setPreferredOrientation更改屏幕方向时有延迟，导致出现弹幕字体大小变化后才旋转屏幕的现象。

import { common } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';

@Entry
@Component
struct VideoPlayer {
  @State @Watch('changeOrientation') isFull: boolean = false; _// 是否全屏_
  @Provide('commentFontSize') myFontSize: number = 14; _// 设置弹幕字体大小_

  changeOrientation() {
    let context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
    window.getLastWindow(context).then((lastWindow) => {
     _ // 设置横/竖屏_
      lastWindow.setPreferredOrientation(this.isFull ? window.Orientation.LANDSCAPE :
        window.Orientation.PORTRAIT);
      this.myFontSize = this.isFull ? 22 : 14; _// 进入/退出全屏时更改字体大小_
    });
  }
}

_// 弹幕组件_
@Component
export struct BulletCommentView {
  @Consume('commentFontSize') myFontSize: number; _// 设置弹幕字体大小_

  build() {
    Stack() {
      if (this.showBulletComment) {
        ForEach(this.bulletComments, (item: BulletCommentItem) => {
          if (item.isUserBulletComment) {
            Text(item.content)
              .fontSize(this.myFontSize);

          } else {
            Text(item.content)
              .fontSize(this.myFontSize);
          }
        });
      }
    }
    .width('100%')
    .height('100%');
  }
}  

#### 分析结论

应用在更改屏幕方向时更改了弹幕的字号，而且setPreferredOrientation更改屏幕方向时有延迟，导致出现弹幕字体大小变化后才旋转屏幕的现象。

#### 修改建议

对视频组件添加onAreaChange，在横竖屏切换组件区域变化时更改弹幕字体大小。

关键部分：

Video({
  src: this.videoSrc,
  controller: this.controller
})
_// 横竖屏切换组件区域变化时更改弹幕字体大小_
  .onAreaChange((oldValue, newValue) => {
    console.info(oldValue.width.toString());
    if (newValue.width > newValue.height) {
      this.myFontSize = 22;
    } else {
      this.myFontSize = 14;
    }
  })

完整示例：

import { util } from '@kit.ArkTS';
import { common } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';

interface VideoPreparedEvent {
  duration: number;
}

interface VideoUpdateEvent {
  time: number;
}

@Component
export struct BulletCommentView {
  @State forceUpdate: boolean = false; _// 强制刷新用_
  @Link bulletComments: BulletCommentItem\[\];
  @Link isPlaying: boolean;
  @Link showBulletComment: boolean;
  private timerId: number = 0;
  @Consume('commentFontSize') myFontSize: number;

  aboutToAppear() {
    this.startAnimation();
  }

  aboutToDisappear() {
    clearInterval(this.timerId);
  }

  build() {
    Stack() {
      if (this.showBulletComment) {
        ForEach(this.bulletComments, (item: BulletCommentItem) => {
          if (item.isUserBulletComment) {
            Text(item.content)
              .fontSize(this.myFontSize)
              .fontColor(item.color)
              .translate({ x: \`${item.translateX}vp\`, y: 0 })
              .position({ y: \`${item.positionY}%\` })
              .backgroundColor('rgba(0, 0, 0, 0.4)')_// 半透明背景_
              .borderRadius(999)
              .padding({
                top: 4,
                left: 8,
                bottom: 4,
                right: 8
              })
              .borderWidth(1)
              .borderColor('rgba(255, 255, 255, 0.6)')
              .key(item.id.toString());
          } else {
            Text(item.content)
              .fontSize(this.myFontSize)
              .fontColor(item.color)
              .translate({ x: \`${item.translateX}vp\`, y: 0 })
              .position({ y: \`${item.positionY}%\` })
              .backgroundColor('rgba(0, 0, 0, 0.4)')
              .borderRadius(999)
              .padding({
                top: 4,
                left: 8,
                bottom: 4,
                right: 8
              })
              .key(item.id.toString());
          }
        });
      }
     _ // 隐藏的刷新触发器_
      Text(this.forceUpdate ? ' ' : '  ')
        .size({ width: 0, height: 0 });
    }
    .width('100%')
    .height('100%');
  }

  _// 启动弹幕动画_
  private startAnimation() {
   _ // 清除已有定时器_
    if (this.timerId) {
      clearInterval(this.timerId);
    }
    _// 使用setInterval实现动画_
    this.timerId = setInterval(() => {
      if (!this.isPlaying) {
        return;
      }
      let needsUpdate = false;
     _ // 更新所有弹幕位置_
      this.bulletComments.forEach(item => {
        const NEWX = item.translateX - item.speed;
        if (NEWX !== item.translateX) {
          item.translateX = NEWX;
          needsUpdate = true;
        }
      });
     _ // 移除超出屏幕的弹幕并触发更新_
      const BEFORELENGTH = this.bulletComments.length;
      this.bulletComments = this.bulletComments.filter(item => item.translateX > -20);
      if (needsUpdate || this.bulletComments.length !== BEFORELENGTH) {
        this.forceUpdate = !this.forceUpdate; _// 触发视图更新_
      }
    }, 16); _// 60fps_
  }
}

class BulletCommentItem {
  private static trackIndex: number = 0;
  id: number;
  content: Resource | string;
  color: string;
  positionY: number;
  translateX: number = 1000; _// 初始位置(右侧)_
  speed: number; _// 移动速度_
  isUserBulletComment: boolean = false; _// 标记是否用户发送_

  constructor(content: Resource | string, isUserBulletComment: boolean = false) {
    this.id = new Date().getTime();
    this.content = content;
    this.color = '#FFFFFF';
    BulletCommentItem.trackIndex = (BulletCommentItem.trackIndex + 1) % 4;
    this.positionY = BulletCommentItem.trackIndex \* 33; _// 计算Y位置_
    this.speed = 2;
    this.isUserBulletComment = isUserBulletComment;
  }
}

@Entry
@Component
struct BulletCommentFont {
  @State isPlaying: boolean = false;
  @State @Watch('changeOrientation') isFull: boolean = false; _// 是否全屏_
  @Provide('commentFontSize') myFontSize: number = 14; _// 设置弹幕字体大小_
  @State currentTime: number = 0; _// 当前播放进度_
  @State duration: number = 0; _// 总时长_
  @State showControls: boolean = true; _// 是否显示控制栏_
  @State showBulletComment: boolean = true; _// 是否显示弹幕_
  @State bulletComments: BulletCommentItem\[\] = \[\];
  private videoSrc: Resource = $rawfile('video.mov'); _// $rawfile('video.mov')需要替换为开发者需要的视频资源文件_
  private controller: VideoController = new VideoController();
  private bulletCommentIndex: number = 0;
  private presetBulletComments = \[
    '很好看', '支持', '再来一个', '爱了爱了', '厉害', '哈哈哈', '太棒了', '666', '精彩！', '好看！', '泪目',
    '针不戳', '好耶', '哈哈哈有趣', '绝了', '妙啊', '点赞', '关注了', '笑死我了', '收藏了', '点我发弹幕吧'
  \];
  private bulletCommentInterval: number = 0;

  aboutToAppear() {
    this.startBulletCommentGenerator();
    let initBulletComments = \[
      '很好看', '支持',
    \];
    this.bulletComments = initBulletComments.map(text => new BulletCommentItem(text));
    let context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
    window.getLastWindow(context).then((lastWindow) => {
      _// 设置沉浸式_
      lastWindow.setWindowLayoutFullScreen(true);
      let systemBarProperties: window.SystemBarProperties = {
        statusBarColor: '#00000000',
        statusBarContentColor: '#ffffff'
      };
      lastWindow.setWindowSystemBarProperties(systemBarProperties);
    });
  }

  _// 横竖屏切换_
  changeOrientation() {
    let context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
    window.getLastWindow(context).then((lastWindow) => {
      lastWindow.setPreferredOrientation(this.isFull ? window.Orientation.AUTO\_ROTATION\_LANDSCAPE :
        window.Orientation.PORTRAIT);
      lastWindow.setWindowSystemBarEnable(this.isFull ? \[\] : \['status', 'navigation'\]);
    });
  }

  aboutToDisappear() {
    clearInterval(this.bulletCommentInterval);
  }

 _ // 切换播放状态_
  private togglePlay() {
    this.isPlaying = !this.isPlaying;
    if (this.isPlaying) {
      this.controller.start();
    } else {
      this.controller.pause();
    }
  }

  _// 根据播放时间均匀生成弹幕_
  private startBulletCommentGenerator() {
   _ // 每3秒生成一条_
    this.bulletCommentInterval = setInterval(() => {
      if (!this.isPlaying) {
        return;
      }
      let randomText = this.presetBulletComments\[this.bulletCommentIndex\];
      this.bulletCommentIndex = (this.bulletCommentIndex + 1) % this.presetBulletComments.length;
      this.bulletComments = \[...this.bulletComments, new BulletCommentItem(randomText)\];
    }, 1000);
  }

 _ // 格式化时间_
  private formatTime(seconds: number): string {
    let mins = Math.floor(seconds / 60);
    let secs = Math.floor(seconds % 60);
    return util.format('%s:%s', mins.toString().padStart(2, '0'), secs.toString().padStart(2, '0'));
  }

  build() {
    Column() {
      Column() {
       _ // 视频播放区域_
        Stack() {
          Video({
            src: this.videoSrc,
            controller: this.controller
          })
         _ // 横竖屏切换组件区域变化时更改弹幕字体大小_
            .onAreaChange((oldValue, newValue) => {
              console.info(oldValue.width.toString());
              if (newValue.width > newValue.height) {
                this.myFontSize = 22;
              } else {
                this.myFontSize = 14;
              }
            })
            .size({ width: '100%', height: '100%' })
            .autoPlay(false)
            .controls(false)
            .onPrepared((event: VideoPreparedEvent) => { _// 使用定义好的接口类型_
              this.controller.start();
              this.duration = event.duration;
            })
            .objectFit(ImageFit.Contain)
            .onUpdate((event: VideoUpdateEvent) => {
              this.currentTime = event.time;
             _ // 同步播放状态到弹幕_
              if (event.time > 0 && !this.isPlaying) {
                this.isPlaying = true;
              }
            })
            .onFinish(() => {
              this.isPlaying = false;
            })
            .onClick(() => {
              this.showControls = !this.showControls;
            });
        _  // 弹幕组件_
          BulletCommentView({
            bulletComments: $bulletComments,
            isPlaying: $isPlaying,
            showBulletComment: $showBulletComment,
          })
            .width('100%')
            .height(100)
            .position({ x: '0%', y: '40%' })
            .onClick(() => {
              this.showControls = !this.showControls;
            });

         _ // 控制栏_
          if (this.showControls) {
            Column() {
             _ // 进度条_
              Row() {
                Slider({
                  value: this.currentTime,
                  min: 0,
                  max: this.duration,
                  step: 1,
                  style: SliderStyle.OutSet
                })
                  .trackThickness(6)
                  .blockSize({
                    width: 16,
                    height: 16
                  })
                  .height(60)
                  .trackColor('rgba(255, 255, 255, 0.4)')
                  .onChange((value: number, mode: SliderChangeMode) => {
                    if (mode === SliderChangeMode.Begin) {
                      this.controller.pause();
                    } else if (mode === SliderChangeMode.End) {
                      this.controller.start();
                    }
                    this.controller.setCurrentTime(value);
                  });
              }
              .height(30)
              .margin({ left: 8, right: 8 });

             _ // 控制按钮_
              Row() {
                Button() {
                  Image(this.isPlaying ? $r('app.media.pause') :
                    $r('app.media.play')) _// $r('app.media.pause')和$r('app.media.play')需要替换为开发者需要的图片资源文件_
                    .width(24)
                    .height(24);
                }
                .onClick(() => this.togglePlay())
                .margin({ right: 8 })
                .backgroundColor(Color.Transparent);

                Button() {
                  Image($r('app.media.next')) _// $r('app.media.next')需要替换为开发者需要的图片资源文件_
                    .width(24)
                    .height(24);
                }
                .backgroundColor(Color.Transparent)
                .margin({ left: 8, right: 8 });

               _ // 控制弹幕显隐_
                Button() {
                  Image(this.showBulletComment ? $r('app.media.bulletComment\_hide') :
                    $r('app.media.bulletComment\_show')) _// $r('app.media.bulletComment\_hide')和$r('app.media.bulletComment\_show')需要替换为开发者需要的图片资源文件_
                    .width(24)
                    .height(24);
                }
                .backgroundColor(Color.Transparent)
                .margin({ left: 8, right: 8 })
                .onClick(() => {
                  this.showBulletComment = !this.showBulletComment;
                });

                Text('  点击发送弹幕')
                  .backgroundColor('rgba(255, 255, 255, 0.3)')
                  .fontSize(14)
                  .padding({ top: 0, bottom: 0 })
                  .fontColor(Color.White)
                  .width(118)
                  .height(24)
                  .margin({ left: 8 })
                  .borderRadius(20);
                Blank();

                Text(this.formatTime(this.currentTime) + '/' + this.formatTime(this.duration))
                  .fontColor(Color.White)
                  .fontSize(14)
                  .margin({ right: 8 });

               _ // $r('app.media.out')和$r('app.media.full')需要替换为开发者需要的图片资源文件_
                Image(this.isFull ? $r('app.media.out') : $r('app.media.full'))
                  .width(20)
                  .height(20)
                  .objectFit(ImageFit.Contain)
                  .onClick(() => {
                    this.isFull = !this.isFull;
                  })
                  .margin({ left: 5 });
              }
              .height('20%')
              .width('100%')
              .padding({ left: 12, right: 12 });
            }
            .backgroundColor('rgba(0, 0, 0, 0.5)')
            .height('40%')
            .width('100%')
            .position({ y: this.isFull ? '75%' : '78%' });
          }
        }
        .height('100%');
      }
      .width('100%')
      .height('100%')
      .expandSafeArea(\[SafeAreaType.SYSTEM, SafeAreaType.KEYBOARD\], \[SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM\]);
    }
    .width('100%')
    .height('108%');
  }
}

效果图如下：

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/d7/v3/v7LK94PhSNGFbgVoIKJi6A/zh-cn_image_0000002628394518.png?HW-CC-KV=V1&HW-CC-Date=20260709T133910Z&HW-CC-Expire=86400&HW-CC-Sign=B4F2394C1A83CCB78E1E7A07F5BEB4BB82BC13D4E9BDEC2AAA26EDC0975BD3D8 "点击放大")