SoFunction
Updated on 2025-04-10

Sample code for implementing pop-up animation zoom to a certain location in WeChat/Alipay applet

HTML

  <view wx:if="{{advertiseFlag}}" class="advertise-wrapper" style="background-color:{{ == 'playing'?'rgba(255,255,255,0)':''}}" bindtap="jumpFn">
    <view class="advertise-box" style="width:{{}};height:{{}};left:{{}};top:{{}};opacity:{{}};animation:{{}}">
      <image data-status="{{}}" catchtap="handleJumpValue" src="{{  || '/certification/2024-06-13/'}}">
      </image>
      <view class="jump-box" catchtap="jumpFn" data-status="{{}}">
        jump over{{defaultTime?defaultTime:''}}
      </view>
    </view>
  </view>

CSS

.advertise-wrapper {
  width: 100%;
  height: 100vh;
  background: rgba(0, 0, 0, 0.75);
  position: fixed;
  top: 0;
  left: 0;
  z-index: 999;
  display: flex;
  justify-content: center;
  align-items: center;
}
.advertise-box {
  width: 580rpx;
  height: 980rpx;
  position: absolute;
  transition: all 1s linear;
}
@keyframes shrinkAndMoveToPosition {
  from {
    transform: scale(1);
    opacity: 1;
  }
  to {
    transform: scale(0.5);
    opacity: 0;
  }
}
.advertise-box image {
  width: 100%;
  height: 100%;
}
.jump-box {
  background: rgba(0, 0, 0, 0.8);
  border-radius: 10px;
  padding: 4rpx 16rpx;
  position: absolute;
  top: 20rpx;
  right: 20rpx;
  color: #fff;
  font-size: 12px;
}

JS

Animation functions

Parameters of options

from:  Start value For example: 0

to : End value For example: 100

TotalMS: Total time of change, for example: 1000

duration : The number of times changes every second  For example: 1

onmove: The callback function that starts moving

oneend: The callback function that ends the move

let timer;
function createAnimation(option) {
  // Start value, end value, total change time  var {
    from,
    to,
    totalMS,
    duration,
    onmove,
    onend
  } = option;
  totalMS = totalMS || 1000;
  duration = duration || 10; // Change every time  var times = (totalMS / duration); // Number of changes  var dis = (to - from) / times; // The amount of change per time  var curTimes = 0;
  // Functions for each change  var timer = setInterval(() => {
    from += dis;
    curTimes++;
    // Changes are completed, here we ensure that onmove is executed before oneend    if (curTimes >= times) {
      from = to;
      onmove && onmove(from);
      onend && onend();
      clearInterval(timer);
      return;
    }
    onmove && onmove(from);
  }, duration);
}

Get dom

When we click to jump, we first need to get the status of the current click dom. If the current status is playing, directly return, otherwise we will start to obtain the current dom information, find the current click dom and the location of the dom you want to jump to, and then find the location you want to jump to, and then pass the currently clicked dom and the dom you want to go to to the animation function that starts

  handleGetDom(type) {
    if (!type || type <= 0) return
    let _this = this
    ().select('.advertise-box').boundingClientRect()
      .selectAll('.grid-container .item').boundingClientRect().exec((ret) => {
        const [popRect, endDoms] = ret;
        const targetIndex = ((item) =>  == type);
        if (targetIndex === -1) return;
        const endDom = endDoms[targetIndex];
        _this.startTransition(popRect, endDom);
      })
  },

Start the animation transition

According to obtaindom And the position of the dom you want to go to, set the status status to playing before you get the dom to end, so that we can set the animation effect and pass the corresponding parameters to the animation function createAnimation.

  // Start the animation transition  startTransition(popRect, endDom) {
    const _this = this;
    // Set the click status to play    _this.setData({
      transitionData: {
        ..._this.,
        statusBtn: 'playing'
      }
    });
    const centerX = 
    const centerY = 
    _this.setData({
      transitionData: {
        ..._this.,
        animation: "shrinkAndMoveToPosition 2s forwards"
      }
    });
    createAnimation({
      from: ,
      to: centerX,
      totalMS: 1000,
      onmove: (n) =&gt; {
        _this.updateTransitionData(endDom, centerX, centerY);
      },
      onend: () =&gt; {
        _this.endTransition(endDom);
      }
    });
  },

Animation update function

It should be noted in this place that in Alipay, there is no need to add px for left and top, and the width and height are determined by yourself whether to divide by 2.

  // Update data during animation  updateTransitionData(endDom, centerX, centerY) {
    ({
      transitionData: {
        ...,
        width: `${ / 2}px`,
        height: `${ / 2}px`,
        left: `${centerX}px`,
        top: `${centerY}px`
      }
    });
  },

The end function of the animation

At the end of the animation, we need to change status to end, opacity to 0, and clear the timer

 // End the animation and handle jump  endTransition(endDom) {
    const _this = this;
    ();
    _this.setData({
      transitionData: {
        ..._this.,
        statusBtn: 'end',
        opacity: 0
      },
      advertiseFlag: false
    });
    clearInterval(timer);
    const currItem = {
      ...,
      richTextType: 2,
      appletAdvertisementId: 
    }
    _this.handleJumpTypePage(currItem);
  },

Animate all related event functions

  // Jump type  handleJumpValue(e) {
    let {
      relationHomeSwitch,
      relationHomeType,
      id
    } = 
    (id)
    if (relationHomeType &amp;&amp; relationHomeSwitch == 1) {
      let status = 
      // Determine whether statusBtn has been clicked      if (status == 'playing') return;
      (relationHomeType)
    } else if (relationHomeSwitch == 2) {
      let data = {
        ...,
        richTextType: 3,
      }
      (data)
    } else {
      ();
      ({
        advertiseFlag: false
      });
      clearInterval(timer);
    }
  },
  // Get dom  handleGetDom(type) {
    if (!type || type &lt;= 0) return
    let _this = this
    ().select('.advertise-box').boundingClientRect()
      .selectAll('.grid-container .item').boundingClientRect().exec((ret) =&gt; {
        const [popRect, endDoms] = ret;
        const targetIndex = ((item) =&gt;  == type);
        if (targetIndex === -1) return;
        const endDom = endDoms[targetIndex];
        _this.startTransition(popRect, endDom);
      })
  },
  // Start the animation transition  startTransition(popRect, endDom) {
    const _this = this;
    // Set the click status to play    _this.setData({
      transitionData: {
        ..._this.,
        statusBtn: 'playing'
      }
    });
    const centerX = 
    const centerY = 
    _this.setData({
      transitionData: {
        ..._this.,
        animation: "shrinkAndMoveToPosition 2s forwards"
      }
    });
    createAnimation({
      from: ,
      to: centerX,
      totalMS: 1000,
      onmove: (n) =&gt; {
        _this.updateTransitionData(endDom, centerX, centerY);
      },
      onend: () =&gt; {
        _this.endTransition(endDom);
      }
    });
  },
  // Update data during animation  updateTransitionData(endDom, centerX, centerY) {
    ({
      transitionData: {
        ...,
        width: `${ / 2}px`,
        height: `${ / 2}px`,
        left: `${centerX}px`,
        top: `${centerY}px`
      }
    });
  },
  // End the animation and handle jump  endTransition(endDom) {
    const _this = this;
    ();
    _this.setData({
      transitionData: {
        ..._this.,
        statusBtn: 'end',
        opacity: 0
      },
      advertiseFlag: false
    });
    clearInterval(timer);
    const currItem = {
      ...,
      richTextType: 2,
      appletAdvertisementId: 
    }
    _this.handleJumpTypePage(currItem);
  },
  // Click  handleHomeConfigClick(id) {
    let params = {
      id: id
    }
    ('/main-service/home-config/click', params).then((res) =&gt; {
      if ( == 1) {
        (res, 'res');
      }
    }).catch((err) =&gt; {
      (err, 'err');
    })
  },
  jumpFn() {
    ();
    ({
      advertiseFlag: false
    });
    clearInterval(timer);
  },
  // Animationend

Complete implementation code

// pages/prize/
const api = require('../../common/api')
const App = getApp()
const getAuthCode = require('../../common/').getAuthCode
let timer;
let count = 0;
function createAnimation(option) {
  // Start value, end value, total change time  var {
    from,
    to,
    totalMS,
    duration,
    onmove,
    onend
  } = option;
  totalMS = totalMS || 1000;
  duration = duration || 10; // Not much time to change once  var times = (totalMS / duration); // Number of changes  var dis = (to - from) / times; // The amount of change per time  var curTimes = 0;
  // Functions for each change  var timer = setInterval(() =&gt; {
    from += dis;
    curTimes++;
    // Changes are completed, here we ensure that onmove is executed before oneend    if (curTimes &gt;= times) {
      from = to;
      onmove &amp;&amp; onmove(from);
      onend &amp;&amp; onend();
      clearInterval(timer);
      return;
    }
    onmove &amp;&amp; onmove(from);
  }, duration);
}
Page({
  data: {
    transitionData: {
      statusBtn: '', // Have you clicked      left: '',
      top: '',
      width: '580rpx',
      height: '980rpx',
      opacity: 1
    },
    marketingId: "", // Marketing id    styleConfigData: {},
    imgSrc: [],
    paramStr: '',
    indicatorDots: false,
    autoplay: true,
    vertical: false,
    interval: 2000,
    circular: true,
    duration: 1500,
    defaultTime: 4, //Default time    advertiseFlag: false,
    advertiseMsg: {},
    activityData: {
      sourceType: ''
    },
    styleHomeImage: {},
    activeIndex: "", // Switch subscript    interval: 3000,
    advertisingRotation: [], // Advertising space carousel    newHomepage: [],
  },
  /**
    * Lifecycle function-listen to page load
    */
  onLoad: function (options) {
    // ()
    // ()
    ()
    ()
    ()
    ()
  },
  onShow() {
    let token = ('token')
    if ( == 'activity' &amp;&amp;  &lt; 2 &amp;&amp; token) {
      ()
    }
  },
  handleActivity() {
    const params = 
    ('/main-service/customer-invite-record', params).then((res) =&gt; {
      if ( === 1) {
        ('Invitation processing was successful')
         = 2
      }
      if ( == 1010002) {
        ('Invitation processing failed')
         = 1
      }
    })
  },
  // Get the home page header configuration  getHomeConfigPage() {
    ("/main-service/home-config/list").then((res) =&gt; {
      if ( == 1) {
        let originalHomepage =  &amp;&amp; ((a, b) =&gt; {
          return  - 
        }) || []
        // Process data, add extra properties for rendering        const processedHomepage = (item =&gt; {
          return {
            ...item,
            shouldDisplay: (item),
            className: (),
            imageMode: (),
            showMenu:  &gt;= 5,
            defaultImage: ()
          };
        });
        ({
          newHomepage: processedHomepage
        })
      }
    }).catch((err) =&gt; {
      (err, 'err');
    })
  },
  // Get page style configuration  getStyleConfig() {
    ('/main-service/activity_style_config').then((res) =&gt; {
      if ( === 1) {
        const {
          data
        } = res
        ({
          styleConfigData: {
            ...data,
            topImg: (",")[0],
            bottomImg: (",")[1],
          }
        })
      }
    })
  },
  //data  handleGetAxiosData() {
    let params = {
      type: 39
    }
    (`/main-service/sys/info`, params).then((res) =&gt; {
      (res, 'res')
      if ( == 1) {
        ({
          styleHomeImage:  ? () : {},
        })
      }
    })
  },
  getImage() {
    ('/main-service/home-page/advertising').then(res =&gt; {
      ({
        imgSrc: 
      })
    })
  },
  // Get the ad space carousel  getAdvertisement() {
    const params = {
      position: 1
    }
    ('/main-service/advertisement', params).then((res) =&gt; {
      if ( == 1) {
        ({
          advertisingRotation: 
        })
      }
    })
  },
  // Change the picture  changeImg(e) {
     = 
    ({
      activeIndex: 
    })
  },
  getAuth() {
    getAuthCode().then(res =&gt; {
      let authCode = ;
      let params = {
        code: authCode
      };
      (params);
    });
  },
  //Get pop-up ad information  getAdvertiseMsg(params) {
    ("/main-service/applet-advertisement/v2", params).then(res =&gt; {
      if ( == 1) {
        ({
          advertiseFlag:  == 1 ? true : false,
          advertiseMsg: ,
          advertisingPopup: {
            ...
          }
        });
        if () {
          ();
        }
        if ( == 1) {
          timer = setInterval(() =&gt; {
            if ( &gt; 0) {
              let time = ( -= 1);
              ({
                defaultTime: time
              });
              return;
            }
            if (!) {
              clearInterval(timer);
              ();
            }
          }, 1000);
        }
      }
    });
  },
  //Is it interested  isLike() {
    let id = ;
    if (id) {
      (`/main-service/applet-advertisement/${id}`).then(res =&gt; {
        if ( == 1) {
          ("interested");
        }
      });
    }
  },
  // Jump details  handleJumpDetails(item) {
    let {
      id,
      jumpType
    } = 
    if (jumpType == 13) {
      ({
        url: `/addressManagement/pages/richText/richText?jumpId=${id}`
      });
    }
  },
  // Page jump  handleJumpToPage(e) {
    let {
      item
    } = 
    ()
    ()
    let data = {
      ...item,
      richTextType: 2,
      appletAdvertisementId: 
    }
    ()
    (data)
  },
  // Process page jump according to jump type  handleJumpTypePage(item) {
    const jumpType = Number();
    ()
    const routes = {
      1: () =&gt; (),
      2: () =&gt; ({
        url: "/addressManagement/pages/invitingWithCourtesy/invitingWithCourtesy"
      }),
      3: () =&gt; ({
        url: `/addressManagement/pages/invitationGiftDetail/invitationGiftDetail?id=${}`
      }),
      4: () =&gt; ({
        url: '/pages/activity/activity'
      }),
      5: () =&gt; ({
        url: `/pages/activityDetail/activityDetail?activityId=${}`
      }),
      6: () =&gt; ({
        url: `/addressManagement/pages/richText/richText?jumpId=${}&amp;type=${}`
      }),
      // 7: () =&gt; ({ url: `/pages/content-detail/content-detail?id=${}&amp;type=1` }),
      // 8: () =&gt; ({ url: `/pages/content-detail/content-detail?id=${}&amp;type=3` }),
      9: () =&gt; ({
        url: `/addressManagement/pages/richText/richText?jumpId=${}&amp;status=btn&amp;type=${}`
      }),
      10: () =&gt; ({
        url: `/addressManagement/pages/marketing/marketing?id=${}&amp;type=home`
      }),
    };
    if (routes[jumpType]) {
      routes[jumpType]();
    }
  },
  // Click on the data  handleClickDataSave(id) {
    let params = {
      appletAdvertisementCustomerRecordId: id
    }
    ('/main-service/applet-advertisement/save-click-data', params).then((res) =&gt; {
      if ( == 1) {
        (res, 'res');
      }
    }).catch((err) =&gt; {
      (err, 'err');
    })
  },
  // Jump type  handleJumpValue(e) {
    let {
      relationHomeSwitch,
      relationHomeType,
      id
    } = 
    (id)
    if (relationHomeType &amp;&amp; relationHomeSwitch == 1) {
      let status = 
      // Determine whether statusBtn has been clicked      if (status == 'playing') return;
      (relationHomeType)
    } else if (relationHomeSwitch == 2) {
      let data = {
        ...,
        richTextType: 3,
      }
      (data)
    } else {
      ();
      ({
        advertiseFlag: false
      });
      clearInterval(timer);
    }
  },
  // Get dom  handleGetDom(type) {
    if (!type || type &lt;= 0) return
    let _this = this
    ().select('.advertise-box').boundingClientRect()
      .selectAll('.grid-container .item').boundingClientRect().exec((ret) =&gt; {
        const [popRect, endDoms] = ret;
        const targetIndex = ((item) =&gt;  == type);
        if (targetIndex === -1) return;
        const endDom = endDoms[targetIndex];
        _this.startTransition(popRect, endDom);
      })
  },
  // Start the animation transition  startTransition(popRect, endDom) {
    const _this = this;
    // Set the click status to play    _this.setData({
      transitionData: {
        ..._this.,
        statusBtn: 'playing'
      }
    });
    const centerX = 
    const centerY = 
    _this.setData({
      transitionData: {
        ..._this.,
        animation: "shrinkAndMoveToPosition 2s forwards"
      }
    });
    createAnimation({
      from: ,
      to: centerX,
      totalMS: 1000,
      onmove: (n) =&gt; {
        _this.updateTransitionData(endDom, centerX, centerY);
      },
      onend: () =&gt; {
        _this.endTransition(endDom);
      }
    });
  },
  // Update data during animation  updateTransitionData(endDom, centerX, centerY) {
    ({
      transitionData: {
        ...,
        width: `${ / 2}px`,
        height: `${ / 2}px`,
        left: `${centerX}px`,
        top: `${centerY}px`
      }
    });
  },
  // End the animation and handle jump  endTransition(endDom) {
    const _this = this;
    ();
    _this.setData({
      transitionData: {
        ..._this.,
        statusBtn: 'end',
        opacity: 0
      },
      advertiseFlag: false
    });
    clearInterval(timer);
    const currItem = {
      ...,
      richTextType: 2,
      appletAdvertisementId: 
    }
    _this.handleJumpTypePage(currItem);
  },
  // Click  handleHomeConfigClick(id) {
    let params = {
      id: id
    }
    ('/main-service/home-config/click', params).then((res) =&gt; {
      if ( == 1) {
        (res, 'res');
      }
    }).catch((err) =&gt; {
      (err, 'err');
    })
  },
  jumpFn() {
    ();
    ({
      advertiseFlag: false
    });
    clearInterval(timer);
  },
  // animation end  handlePageTo: function () {
    let that = this
    ({
      title: 'loading',
      mask: true
    })
    ()
    // ()
  },
  // getUserInfo() {
  //   let that = this
  //   getAuthCode().then((res) =&gt; {
  //     const {
  //       authCode
  //     } = res
  //     let params = {
  //       loginType: 7,
  //       isRelatedPhoneNumber: 0,
  //       grantCode: authCode
  //     }
  //     ('/main-service/customer/login', params).then(({
  //       data
  //     }) =&gt; {
  //       const {
  //         isRelatedPhoneNumber,
  //         phoneNumber,
  //         token
  //       } = data
  //       if (isRelatedPhoneNumber == 0) {
  //         ()
  //         ({
  //           url: '/pages/login/login?sourceType=prize'
  //         });
  //         return
  //       }
  //       if (isRelatedPhoneNumber == 1) {
  //         ()
  //         ('token', token)
  //         ()
  //         return
  //       }
  //     })
  //   })
  // },
  checkAttestation() {
    ('/main-service/pregnant-certification/status').then(({
      data
    }) =&gt; {
      ()
      const {
        isWhite,
        isSellOut,
        status,
        identityType,
        isSyncTaoBao
      } = data
      if (isWhite == 0) {
        if (status == 0) {
          // Jump to the authentication page          ({
            url: '/pages/authentication/authentication'
          })
        }
        if (status == 1) {
          // Jump authentication          ({
            url: '/pages/AddCustomer/AddCustomer'
          })
        }
        if (status == 2) {
          // Open the collection link          if (isSellOut) {
            // The gifts are sold out            // "identityType": 1: Pregnant mother 2: Baoma            if (!isSyncTaoBao) {
              ({
                url: '/pages/sellOut/sellOut'
              })
              // if (identityType == 1) {
              //   ({
              //     url: '/pages/sellOut/sellOut'
              //   })
              // }
              // if (identityType == 2) {
              //   ({
              //     url: `/pages/certificationPassed/certificationPassed`
              //   })
              // }
            } else {
              // Modified logic              ({
                url: `/pages/giftGuide/giftGuide?status=${status}`
              })
            }
          } else {
            ({
              url: `/pages/giftGuide/giftGuide?status=${status}`
            })
            // if (identityType == 1) {
            //   ({
            //     url: `/pages/giftGuide/giftGuide?status=${status}`
            //   })
            // }
            // if (identityType == 2) {
            //   ({
            //     url: `/pages/certificationPassed/certificationPassed`
            //   })
            // }
          }
        }
        if (status == 3) {
          // Jump to the rejection page          ({
            url: '/pages/certificationReject/certificationReject'
          })
        }
        if (status == 4) {
          // Jump back to the page to edit again          ({
            url: `/pages/authentication/authentication?status=${status}`
          })
        }
      }
      if (isWhite == 1) {
        if (isSellOut) {
          ({
            url: '/pages/sellOut/sellOut'
          })
        } else {
          ({
            url: `/pages/giftGuide/giftGuide?status=${status}`
          })
        }
      }
    })
  },
  handleClickBanner() {
    ({
      url: '/addressManagement/pages/bannerDetail/bannerDetail',
    })
  },
  // Jump to the venue  handleToShare() {
    ({
      url: `/addressManagement/pages/taobaoVenue/taobaoVenue?type=home`,
    });
  },
  // Turn on sharing  onShareAppMessage() {},
  // Determine whether to display the item  shouldDisplayItem(item) {
    if ( &gt;= 5 &amp;&amp;  !== 1) return false;
    return !! ||  &lt;= 4; // Always displayed when type is less than or equal to 4  },
  // Return the corresponding class name according to type  getClassByType(type) {
    switch (type) {
      case 1:
        return 'new-mom-gift';
      case 2:
        return 'xhs-volunteer';
      case 3:
        return 'douyin-volunteer';
      case 4:
        return 'invitation-gift';
      case 5:
        return 'advertising-space-one';
      case 6:
        return 'advertising-space-two';
      case 7:
        return 'advertising-space-three';
      default:
        return '';
    }
  },
  // Return to the mode of the image according to type  getImageMode(type) {
    return type &gt;= 5 ? 'widthFix' : 'scaleToFill';
  },
  // Get the default image URL  getDefaultImage(type) {
    return '/certification/2024-06-13/';
  },
  onPullDownRefresh() {
    ([()]).then(res =&gt; {
      ();
    });
  },
  onUnload() {
    ()
  },
})

This is the article about the WeChat/Alipay mini program that implements pop-up animation zoom to a certain position. For more related pop-up animation zoom to a certain position, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!