zhangbo1_wd

新增顶导新方案

  1 +import { BottomNavDTO, CompDTO, TopNavDTO } from 'wdBean';
  2 +import { SpConstants } from 'wdConstant';
  3 +import { DisplayUtils, LazyDataSource, Logger, NetworkUtil, SPHelper, ToastUtils } from 'wdKit';
  4 +import { ProcessUtils, WDRouterPage, WDRouterRule } from 'wdRouter';
  5 +import { PageComponent } from './PageComponent';
  6 +import { ChannelSubscriptionLayout } from './ChannelSubscriptionLayout';
  7 +import { FirstTabTopSearchComponent } from '../search/FirstTabTopSearchComponent';
  8 +import { AssignChannelParam } from 'wdRouter/src/main/ets/utils/HomeChannelUtils';
  9 +import { PeopleShipMainComponent } from '../peopleShip/PeopleShipMainComponent';
  10 +import { channelSkeleton } from '../skeleton/channelSkeleton';
  11 +
  12 +const TAG = 'TopNavigationComponent';
  13 +
  14 +PersistentStorage.persistProp('channelIds', '');
  15 +PersistentStorage.persistProp('indexSettingChannelId', 2002);
  16 +
  17 +const storage = LocalStorage.getShared();
  18 +
  19 +/**
  20 + * 顶部页签导航栏/顶导
  21 + */
  22 +@Entry(storage)
  23 +@Component
  24 +export struct TopNavigationComponentNew {
  25 + /**
  26 + * @deprecated
  27 + */
  28 + private groupId: number = 0
  29 + /**
  30 + * @deprecated TODO type判断
  31 + */
  32 + private currentBottomNavName: string = ''
  33 + private swiperController: SwiperController = new SwiperController()
  34 + private listScroller: Scroller = new Scroller()
  35 + @Consume barBackgroundColor: Color
  36 + /**
  37 + * @deprecated
  38 + */
  39 + @State bottomSafeHeight: number = AppStorage.get<number>('bottomSafeHeight') || 0
  40 + /**
  41 + * @deprecated
  42 + */
  43 + @State topSafeHeight: number = AppStorage.get<number>('topSafeHeight') || 0
  44 + @Link _currentNavIndex?: number;
  45 + // 顶导当前选中/焦点下标
  46 + @State currentTopNavSelectedIndex: number = 0;
  47 + // 顶导数据
  48 + @State @Watch('onTopNavigationDataUpdated') topNavList: TopNavDTO[] = []
  49 + @StorageProp('indexSettingChannelId') indexSettingChannelId: number = 2002
  50 + //我的频道id列表
  51 + @State channelIds: number[] = []
  52 + //本地缓存频道id列表
  53 + @StorageProp('channelIds') storageChannelIds: string = ''
  54 + @State homeChannelList: TopNavDTO[] = []
  55 + // 我的频道列表
  56 + @State myChannelList: TopNavDTO[] = []
  57 + // 更多频道列表
  58 + @State moreChannelList: TopNavDTO[] = []
  59 + // 地方频道列表
  60 + @State localChannelList: TopNavDTO[] = []
  61 + readonly MAX_LINE: number = 1;
  62 + @ObjectLink @Watch('onAssignChannelChange') assignChannel: AssignChannelParam
  63 + // 底导传递过来的自动刷新通知
  64 + @Prop @Watch('onAutoRefresh') autoRefresh: number = 0
  65 + // 传递给page的自动刷新通知
  66 + @State autoRefresh2Page: number = 0
  67 + // 当前底导index
  68 + @State bottomNavIndex: number = 0
  69 + @State animationDuration: number = 0
  70 + @State indicatorLeftMargin: number = 0
  71 + @State indicatorWidth: number = 0
  72 + private tabsWidth: number = 0
  73 +
  74 + build() {
  75 + // 频道分类list
  76 + RelativeContainer() {
  77 + this.tabBar()
  78 + Swiper(this.swiperController) {
  79 + ForEach(this.currentBottomNavName === '新闻' ? this.myChannelList : this.topNavList,
  80 + (navItem: TopNavDTO, index: number) => {
  81 + if (this.currentBottomNavName === '人民号' && navItem.name === '关注') {
  82 + PeopleShipMainComponent({
  83 + currentTopNavSelectedIndex: $currentTopNavSelectedIndex,
  84 + navIndex: index,
  85 + pageId: navItem.pageId + '',
  86 + channelId: navItem.channelId + '',
  87 + })
  88 + } else
  89 + if (!this.isBroadcast(navItem) && !this.isLayout(navItem)) {
  90 + PageComponent({
  91 + currentTopNavSelectedIndex: $currentTopNavSelectedIndex,
  92 + navIndex: index,
  93 + pageId: navItem.pageId + '',
  94 + channelId: navItem.channelId + '',
  95 + autoRefresh: this.autoRefresh2Page
  96 + })
  97 + } else {
  98 + channelSkeleton()
  99 + }
  100 + })
  101 + }
  102 + .loop(false)
  103 + .indicator(false)
  104 + .effectMode(EdgeEffect.None)
  105 + .index(this.currentTopNavSelectedIndex)
  106 + .cachedCount(0)
  107 + .id('pageContent')
  108 + .alignRules({
  109 + 'top': { 'anchor': 'tabList', 'align': VerticalAlign.Bottom },
  110 + 'middle': { 'anchor': '__container__', 'align': HorizontalAlign.Center },
  111 + 'bottom': { 'anchor': '__container__', 'align': VerticalAlign.Bottom }
  112 + })
  113 + .onChange((index) => {
  114 + Logger.info(TAG, `onChange index : ${index}`);
  115 + if (this.isBroadcastByIndex(index)) {
  116 + // 跳转到播报页面
  117 + ProcessUtils.gotoBroadcastPage(this.myChannelList[index].pageId)
  118 + let nextIndex = this.currentTopNavSelectedIndex > index ? index - 1 : index + 1
  119 + if (nextIndex < this.myChannelList.length) {
  120 + this.changePage(nextIndex)
  121 + }
  122 + } else if (this.isLayoutByIndex(index)) {
  123 + ProcessUtils.gotoENewsPaper()
  124 + let nextIndex = this.currentTopNavSelectedIndex > index ? index - 1 : index + 1
  125 + if (nextIndex < this.myChannelList.length) {
  126 + this.changePage(nextIndex)
  127 + }
  128 + } else {
  129 + this.currentTopNavSelectedIndex = index;
  130 + this.changePage(this.currentTopNavSelectedIndex)
  131 + }
  132 +
  133 + })
  134 + }
  135 + .width('100%')
  136 + .height('100%')
  137 + }
  138 +
  139 + @Builder
  140 + topBar() {
  141 + Column() {
  142 + Row() {
  143 + FirstTabTopSearchComponent()
  144 +
  145 + Image($r('app.media.icon_ren_min_ri_bao'))
  146 + .width(72)
  147 + .height(29)
  148 + .onClick(() => {
  149 + ProcessUtils.gotoENewsPaper()
  150 + })
  151 + Stack({ alignContent: Alignment.Center }) {
  152 + Image($r('app.media.background_read_paper_home'))
  153 + .width('100%')
  154 + .height('100%')
  155 + .objectFit(ImageFit.Contain)
  156 + Row() {
  157 + Image($r('app.media.icon_read_paper_home'))
  158 + .width(18)
  159 + .height(18)
  160 + Text('早晚报')
  161 + .fontColor("#666666")
  162 + .fontSize($r('app.float.font_size_13'))
  163 + }
  164 + .alignItems(VerticalAlign.Center)
  165 + .justifyContent(FlexAlign.Center)
  166 + }
  167 + .height(30)
  168 + .width(124)
  169 + .onClick(() => {
  170 + if (NetworkUtil.isNetConnected()) {
  171 + ProcessUtils.gotoMorningEveningPaper()
  172 + } else {
  173 + ToastUtils.showToast('网络出小差了,请检查网络后重试', 1000)
  174 + }
  175 + })
  176 + }.width('100%')
  177 + .justifyContent(FlexAlign.SpaceBetween)
  178 + }
  179 + .width('100%')
  180 + .height(40)
  181 + .padding({ top: 10 })
  182 + .backgroundColor($r('app.color.white'))
  183 + .id('topBar')
  184 + }
  185 +
  186 + @Builder
  187 + tabBar() {
  188 + // TODO 判断是否新闻tab,修改方法(,_currentNavIndex==0 都不对,需要用type)(用topStyle)
  189 + if (this.currentBottomNavName === '新闻') {
  190 + // 顶部搜索、日报logo、早晚报
  191 + this.topBar()
  192 + ChannelSubscriptionLayout({
  193 + currentTopNavSelectedIndex: $currentTopNavSelectedIndex,
  194 + indexSettingChannelId: this.indexSettingChannelId,
  195 + homeChannelList: this.homeChannelList,
  196 + myChannelList: $myChannelList,
  197 + moreChannelList: $moreChannelList,
  198 + localChannelList: $localChannelList,
  199 + channelIds: $channelIds,
  200 + changeTab: (index) => {
  201 + this.changePage(index)
  202 + }
  203 + })
  204 + .id('channelManageBtn')
  205 + .alignRules({
  206 + 'top': { 'anchor': 'topBar', 'align': VerticalAlign.Bottom },
  207 + 'right': { 'anchor': '__container__', 'align': HorizontalAlign.End }
  208 + })
  209 +
  210 + List({ scroller: this.listScroller }) {
  211 + ForEach(this.myChannelList, (navItem: TopNavDTO, index: number) => {
  212 + ListItem() {
  213 + this.tabBarBuilder(navItem, index)
  214 + }
  215 + })
  216 + }
  217 + .listDirection(Axis.Horizontal)
  218 + .scrollBar(BarState.Off)
  219 + .edgeEffect(EdgeEffect.None)
  220 + .padding({ left: 8, top: 6, right: 0 })
  221 + .height($r('app.float.top_tab_bar_height'))
  222 + .backgroundColor(this.barBackgroundColor)
  223 + .onAreaChange((oldValue: Area, newValue: Area) => {
  224 + let width = Number.parseFloat(newValue.width.toString())
  225 + this.tabsWidth = Number.isNaN(width) ? 0 : width
  226 + })
  227 + .id('tabList')
  228 + .alignRules({
  229 + 'top': { 'anchor': 'topBar', 'align': VerticalAlign.Bottom },
  230 + 'left': { 'anchor': '__container__', 'align': HorizontalAlign.Start },
  231 + 'right': { 'anchor': 'channelManageBtn', 'align': HorizontalAlign.Start }
  232 + })
  233 + } else {
  234 + Row() {
  235 + Image($r('app.media.icon_search'))
  236 + .width('24vp')
  237 + .height('24vp')
  238 + }
  239 + .height('40vp')
  240 + .width('40vp')
  241 + .margin({ right: 10 })
  242 + .alignItems(VerticalAlign.Center)
  243 + .justifyContent(FlexAlign.Center)
  244 + .id('searchBtn')
  245 + .alignRules({
  246 + 'top': { 'anchor': '__container__', 'align': VerticalAlign.Top },
  247 + 'right': { 'anchor': '__container__', 'align': HorizontalAlign.End }
  248 + })
  249 + .onClick(() => {
  250 + WDRouterRule.jumpWithPage(WDRouterPage.searchPage)
  251 + })
  252 +
  253 + List({ scroller: this.listScroller }) {
  254 + ForEach(this.topNavList, (navItem: TopNavDTO, index: number) => {
  255 + ListItem() {
  256 + this.tabBarBuilder(navItem, index)
  257 + }
  258 + })
  259 + }
  260 + .listDirection(Axis.Horizontal)
  261 + .scrollBar(BarState.Off)
  262 + .edgeEffect(EdgeEffect.None)
  263 + .height($r('app.float.top_tab_bar_height'))
  264 + .backgroundColor(this.barBackgroundColor)
  265 + .padding({ top: 6 })
  266 + .onAreaChange((oldValue: Area, newValue: Area) => {
  267 + let width = Number.parseFloat(newValue.width.toString())
  268 + this.tabsWidth = Number.isNaN(width) ? 0 : width
  269 + })
  270 + .id('tabList')
  271 + .alignRules({
  272 + 'top': { 'anchor': '__container__', 'align': VerticalAlign.Top },
  273 + 'middle': { 'anchor': '__container__', 'align': HorizontalAlign.Center }
  274 + })
  275 + }
  276 + }
  277 +
  278 + @Builder
  279 + tabBarBuilder(item: TopNavDTO, index: number) {
  280 + Column() {
  281 + Text(item?.name)
  282 + .fontSize($r('app.float.selected_text_size'))
  283 + .fontWeight(this.currentTopNavSelectedIndex === index ? FontWeight.Bold : FontWeight.Normal)
  284 + .fontColor(this.currentTopNavSelectedIndex === index ? Color.Black : "#999999")
  285 + .padding({ top: $r('app.float.top_tab_item_padding_top'), bottom: $r('app.float.top_tab_item_padding_bottom') })
  286 + .maxLines(this.MAX_LINE)
  287 + .id(index.toString())
  288 + .onAreaChange((oldValue: Area, newValue: Area) => {
  289 + if (this.currentTopNavSelectedIndex === index &&
  290 + (this.indicatorLeftMargin === 0 || this.indicatorWidth === 0)) {
  291 + if (newValue.position.x != undefined) {
  292 + let positionX = Number.parseFloat(newValue.position.x.toString())
  293 + this.indicatorLeftMargin = Number.isNaN(positionX) ? 0 : positionX
  294 + }
  295 + let width = Number.parseFloat(newValue.width.toString())
  296 + this.indicatorWidth = Number.isNaN(width) ? 0 : width
  297 + }
  298 + })
  299 + if (this.currentTopNavSelectedIndex === index) {
  300 + Row()
  301 + .width(20)
  302 + .height(3)
  303 + .backgroundImage($r('app.media.icon_channel_active'), ImageRepeat.NoRepeat)
  304 + .backgroundImageSize(ImageSize.Contain)
  305 + }
  306 + }
  307 + .hoverEffect(HoverEffect.Highlight)
  308 + .constraintSize({
  309 + minWidth: $r('app.float.top_tab_item_min_width'),
  310 + maxWidth: $r('app.float.top_tab_item_max_width')
  311 + })
  312 + // .backgroundColor(Color.Transparent)
  313 + .padding({
  314 + left: $r('app.float.top_tab_item_padding_horizontal'),
  315 + right: $r('app.float.top_tab_item_padding_horizontal'),
  316 + bottom: 2,
  317 + })
  318 + .id(`col_tabBar${index}`)
  319 + .margin({ right: this.myChannelList.length === index + 1 ? 36 : 0 })
  320 + .onClick(() => {
  321 + Logger.debug(TAG, `onClick, index: ${index}`);
  322 + if (this.currentTopNavSelectedIndex === index) {
  323 + // 当前tab,单击事件
  324 + this.doAutoRefresh()
  325 + } else {
  326 + if (this.isBroadcastByIndex(index)) {
  327 + // 跳转到播报页面
  328 + ProcessUtils.gotoBroadcastPage(this.myChannelList[index].pageId)
  329 + } else if (this.isLayoutByIndex(index)) {
  330 + ProcessUtils.gotoENewsPaper()
  331 + } else {
  332 + this.currentTopNavSelectedIndex = index
  333 + this.changePage(index)
  334 + }
  335 + }
  336 + })
  337 + }
  338 +
  339 + private changePage(index: number) {
  340 + this.swiperController.changeIndex(index)
  341 + this.listScroller.scrollToIndex(index, true, ScrollAlign.CENTER)
  342 + }
  343 +
  344 + //处理新闻tab顶导频道数据
  345 + topNavListHandle() {
  346 + let cityName = SPHelper.default.getSync(SpConstants.LOCATION_CITY_NAME, '') as string
  347 +
  348 + let _channelIds: number [] = []
  349 + let _myChannelList: TopNavDTO [] = []
  350 + let _storageChannelIds: string [] = [] //list1
  351 + let defaultMyChannelList: TopNavDTO[] = []
  352 + let defaultList = [...this.topNavList]
  353 + defaultList.sort((a, b) => {
  354 + return a.num - b.num;
  355 + });
  356 +
  357 + //defaultMyChannelList
  358 + defaultList.forEach(item => {
  359 + if (item.defaultPermitted === 1 || item.movePermitted === 0 || item.delPermitted === 0 ||
  360 + item.headlinesOn === 1) {
  361 + defaultMyChannelList.push(item);
  362 + }
  363 + if (item.defaultPermitted === 1) {
  364 + this.homeChannelList.push(item)
  365 + }
  366 + })
  367 +
  368 + //有缓存频道id
  369 + if (this.storageChannelIds) {
  370 + _storageChannelIds = this.storageChannelIds.split(',')
  371 + }
  372 +
  373 + defaultMyChannelList.forEach(item => {
  374 + item.myChannel = '1'
  375 + if (item.defaultPermitted === 1) {
  376 + item.homeChannel = '1'
  377 + }
  378 + let index = defaultList.findIndex(_item => _item.channelId === item.channelId)
  379 + if (index !== -1) {
  380 + defaultList.splice(index, 1)
  381 + }
  382 + })
  383 + defaultList.unshift(...defaultMyChannelList)
  384 +
  385 + defaultList.forEach((item, index) => {
  386 + if (this.storageChannelIds && _storageChannelIds.includes(String(item.channelId))) {
  387 + item.myChannel = '1'
  388 + }
  389 + if (item.channelType === 2) {
  390 + if (cityName.includes(item.name)) {
  391 + item.myChannel = '1'
  392 + }
  393 + item.localChannel = '1'
  394 + }
  395 + if (index >= 11) {
  396 + if (item.channelType === 1) {
  397 + item.moreChannel = '1'
  398 + }
  399 + } else {
  400 + if (item.channelType === 1 && item.myChannel !== '1') {
  401 + item.moreChannel = '1'
  402 + }
  403 + }
  404 +
  405 + //频道分类
  406 + if (item.name !== '播报') { //暂时隐藏播报
  407 + if (item.myChannel === '1') {
  408 + _myChannelList.push(item)
  409 + _channelIds.push(item.channelId)
  410 + } else if (item.moreChannel === '1') {
  411 + this.moreChannelList.push(item)
  412 + } else if (item.localChannel === '1' && item.myChannel !== '1') {
  413 + this.localChannelList.push(item)
  414 + }
  415 + }
  416 +
  417 + })
  418 +
  419 + if (cityName) {
  420 + let index = _myChannelList.findIndex(ele => cityName.includes(ele.name))
  421 + const localChannelitem = _myChannelList.splice(index, 1)[0];
  422 + // 将当前地区频道插入到第四个
  423 + _myChannelList.splice(3, 0, localChannelitem);
  424 + }
  425 +
  426 + this.channelIds = _channelIds
  427 + this.myChannelList = _myChannelList
  428 +
  429 + //缓存首页频道
  430 + let index = this.myChannelList.findIndex(_item => _item?.channelId === this.indexSettingChannelId)
  431 + if (index > -1) {
  432 + this.currentTopNavSelectedIndex = index
  433 + }
  434 + }
  435 +
  436 + private isBroadcast(item?: TopNavDTO): boolean {
  437 + // TODO 用id channelId = '2066'
  438 + return item?.name === '播报'
  439 + }
  440 +
  441 + private isLayout(item?: TopNavDTO): boolean {
  442 + // TODO 用id channelId = '2006'
  443 + return item?.name === '版面'
  444 + }
  445 +
  446 + private isBroadcastByIndex(index: number): boolean {
  447 + let item = this._currentNavIndex === 0 ? this.myChannelList[index] : this.topNavList[index]
  448 + return this.isBroadcast(item)
  449 + }
  450 +
  451 + private isLayoutByIndex(index: number): boolean {
  452 + let item = this._currentNavIndex === 0 ? this.myChannelList[index] : this.topNavList[index]
  453 + return this.isLayout(item)
  454 + }
  455 +
  456 + private isSpecialChannel(item?: TopNavDTO) {
  457 + // 版面、播报,可以用这个判断
  458 + return item?.channelType === 3
  459 + }
  460 +
  461 + aboutToAppear() {
  462 + //处理新闻tab顶导频道数据
  463 + this.topNavListHandle()
  464 + this.changePage(this.currentTopNavSelectedIndex)
  465 + }
  466 +
  467 + aboutToDisappear() {
  468 + AppStorage.set('channelIds', this.channelIds.join(','))
  469 + }
  470 +
  471 + onTopNavigationDataUpdated() {
  472 + Logger.info(TAG,
  473 + `onTopNavigationDataUpdated currentTopNavIndex: ${this.currentTopNavSelectedIndex},topNavList.length:${this.topNavList.length}`);
  474 + }
  475 +
  476 + onAutoRefresh() {
  477 + if (this.bottomNavIndex != this._currentNavIndex) {
  478 + return
  479 + }
  480 + // 通知page刷新
  481 + this.autoRefresh2Page++
  482 + }
  483 +
  484 + private doAutoRefresh() {
  485 + // 通知page刷新
  486 + this.autoRefresh2Page++
  487 + }
  488 +
  489 + /**
  490 + * 频道id变化,即指定频道跳转场景
  491 + */
  492 + onAssignChannelChange() {
  493 + let channelId = this.assignChannel.channelId
  494 + let index = -1
  495 + if (this._currentNavIndex === 0) {
  496 + // 第一个,新闻,先拿我的,再拿其他
  497 + index = this.getChannelByMine(channelId)
  498 + if (index == -1) {
  499 + // 不在我的里,需要临时新增频道展示
  500 + let channel = this.getChannelByOthers(channelId)
  501 + if (channel) {
  502 + this.myChannelList.push(channel)
  503 + setTimeout(() => {
  504 + this.changePage(this.myChannelList.length - 1)
  505 + }, 20)
  506 + }
  507 + } else {
  508 + // 直接切换
  509 + this.changePage(index)
  510 + }
  511 + } else {
  512 + index = this.getChannelByTopNav(channelId)
  513 + if (index > -1) {
  514 + // 找到了,直接切换,否则不处理
  515 + this.changePage(index)
  516 + }
  517 + }
  518 +
  519 + }
  520 +
  521 + /**
  522 + * 非新闻,从topNav里拿数据
  523 + */
  524 + private getChannelByTopNav(channelId: string) {
  525 + for (let i = 0; i < this.topNavList.length; i++) {
  526 + let topNavDTO: TopNavDTO = this.topNavList[i]
  527 + if (topNavDTO.channelId.toString() === channelId) {
  528 + return i
  529 + }
  530 + }
  531 + return -1
  532 + }
  533 +
  534 + /**
  535 + * 新闻,从myChannelList里拿数据
  536 + */
  537 + private getChannelByMine(channelId: string) {
  538 + for (let i = 0; i < this.myChannelList.length; i++) {
  539 + let topNavDTO: TopNavDTO = this.myChannelList[i]
  540 + if (topNavDTO.channelId?.toString() === channelId) {
  541 + return i
  542 + }
  543 + }
  544 + return -1
  545 + }
  546 +
  547 + /**
  548 + * 新闻,从其他里拿数据
  549 + */
  550 + private getChannelByOthers(channelId: string) {
  551 + for (let i = 0; i < this.moreChannelList.length; i++) {
  552 + let topNavDTO: TopNavDTO = this.moreChannelList[i]
  553 + if (topNavDTO.channelId?.toString() === channelId) {
  554 + return topNavDTO
  555 + }
  556 + }
  557 + for (let j = 0; j < this.localChannelList.length; j++) {
  558 + let topNavDTO: TopNavDTO = this.localChannelList[j]
  559 + if (topNavDTO.channelId?.toString() === channelId) {
  560 + return topNavDTO
  561 + }
  562 + }
  563 + return null
  564 + }
  565 +
  566 + private getTextInfo(index: number): Record<string, number> {
  567 + let strJson = getInspectorByKey(index.toString())
  568 + try {
  569 + let obj: Record<string, string> = JSON.parse(strJson)
  570 + let rectInfo: number[][] = JSON.parse('[' + obj.$rect + ']')
  571 + return { 'left': px2vp(rectInfo[0][0]), 'width': px2vp(rectInfo[1][0] - rectInfo[0][0]) }
  572 + } catch (error) {
  573 + return { 'left': 0, 'width': 0 }
  574 + }
  575 + }
  576 +
  577 + private getCurrentIndicatorInfo(index: number, event: TabsAnimationEvent): Record<string, number> {
  578 + let nextIndex = index
  579 + if (index > 0 && event.currentOffset > 0) {
  580 + nextIndex--
  581 + } else if (index < 3 && event.currentOffset < 0) {
  582 + nextIndex++
  583 + }
  584 + let indexInfo = this.getTextInfo(index)
  585 + let nextIndexInfo = this.getTextInfo(nextIndex)
  586 + let swipeRatio = Math.abs(event.currentOffset / this.tabsWidth)
  587 + let currentIndex = swipeRatio > 0.5 ? nextIndex : index // 页面滑动超过一半,tabBar切换到下一页。
  588 + let currentLeft = indexInfo.left + (nextIndexInfo.left - indexInfo.left) * swipeRatio
  589 + let currentWidth = indexInfo.width + (nextIndexInfo.width - indexInfo.width) * swipeRatio
  590 + return { 'index': currentIndex, 'left': currentLeft, 'width': currentWidth }
  591 + }
  592 +
  593 + private startAnimateTo(duration: number, leftMargin: number, width: number) {
  594 + animateTo({
  595 + duration: duration, // 动画时长
  596 + curve: Curve.Linear, // 动画曲线
  597 + iterations: 1, // 播放次数
  598 + playMode: PlayMode.Normal, // 动画模式
  599 + onFinish: () => {
  600 + console.info('play end')
  601 + }
  602 + }, () => {
  603 + this.indicatorLeftMargin = leftMargin
  604 + this.indicatorWidth = width
  605 + })
  606 + }
  607 +}