<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          卡片放大播放效果實(shí)現(xiàn)總結(jié),不同元素間的放大過渡效果如何實(shí)現(xiàn)?

          共 8494字,需瀏覽 17分鐘

           ·

          2023-01-09 20:20

          背景

          最近一段時(shí)間做了幾個(gè)需求,其中涉及的素材列表展示,需要實(shí)現(xiàn)類似下方圖片中的動(dòng)效,暫且稱之為【卡片放大播放動(dòng)效】,具體細(xì)節(jié)如下:

          1. 初始展示的是封面圖片,鼠標(biāo)經(jīng)過時(shí)是視頻放大的效果;
          2. 下方文字內(nèi)容區(qū)域,在放大前后展示的內(nèi)容不同,而且兩者的字體大小是一致的,不是簡單的放大實(shí)現(xiàn);
          3. 四五個(gè)頁面都需要實(shí)現(xiàn)相同列表效果,列表的行數(shù)和列數(shù)是不一致的;
          image

          遇到的四個(gè)問題

          在整個(gè)實(shí)現(xiàn)過程中,遇到以下四個(gè)問題,后面的解析中會(huì)有對應(yīng)解答。

          1. 不同元素間的放大過渡效果如何實(shí)現(xiàn)?
          2. 抽離為通用性組件時(shí),如何實(shí)現(xiàn)類vue中的具名插槽效果,來替換下方文字信息區(qū)域?
          3. 不同頁面中的列表區(qū)域?qū)挾炔煌械捻撁媪斜磉€是彈性寬度,常用的flex布局無法滿足要求,如何實(shí)現(xiàn)呢?
          4. 邊界卡片放大后,如何避免被父級列表容器overflow:hidden`隱藏掉?

          實(shí)現(xiàn)解析

          這個(gè)動(dòng)效,在愛奇藝官網(wǎng)也有類似效果,愛奇藝官網(wǎng)是通過生成初始狀態(tài)卡片列表和鼠標(biāo)放大卡片列表兩套列表,然后通過動(dòng)態(tài)計(jì)算放大卡片位置,相對于body進(jìn)行絕對定位展示的。

          本實(shí)現(xiàn)方案通過將卡片初始狀態(tài)和放大狀態(tài),封裝到一個(gè)組件中,通過兩者間的相對關(guān)系,利用css自動(dòng)完成對應(yīng)關(guān)系,避免了大量的JS計(jì)算。

          1. 放大效果實(shí)現(xiàn)

          UI對該動(dòng)效的要求實(shí)際就是鼠標(biāo)視頻放大播放,如果卡片初始狀態(tài)也放置視頻video,通過transition對同一元素進(jìn)行scale放大也可以實(shí)現(xiàn),但是這是一個(gè)列表,用戶進(jìn)入頁面,就會(huì)同時(shí)加載多個(gè)視頻,用戶體驗(yàn)不是很好。

          所以,實(shí)現(xiàn)方案就是卡片初始狀態(tài)放置poster圖片,鼠標(biāo)經(jīng)過時(shí),在poster上方展示視頻層(絕對定位),然后對視頻執(zhí)行animation動(dòng)畫來模擬放大效果。

          卡片底部文字區(qū)域如何處理?

          由于卡片初始狀態(tài)下,底部文字區(qū)域在列表布局中是占位的,所以在卡片初始狀態(tài)下,底部文字區(qū)域使用正常文檔流。

          卡片鼠標(biāo)經(jīng)過狀態(tài)下,視頻層的放大效果是以poster中心點(diǎn)為放大原點(diǎn)的,所以底部文字區(qū)域使用absolute定位,相對于player進(jìn)行定位處理。

          interface IItemData {
            src?: string;
            poster?: string;
            industry?: string;
          }

          interface IProps {
            posterClass: string; // poster區(qū)域?qū)捀?br>  playerClass: string; // 放大后視頻寬高樣式
            itemData: IItemData;
            children: { [key: string]: any };
          }
          let t: NodeJS.Timeout;

          const VideoItem: FC<IProps> = ({
            posterClass,
            playerClass,
            itemData,
            children,
          }) => {
            const [isHover, setIsHover] = useState(false);
            const onMouseEnter = () => {
              t = setTimeout(() => {
                // 避免快速移動(dòng)鼠標(biāo),造成視頻無法隱藏問題
                setIsHover(true);
              }, 50);
            };
            const onMouseLeave = () => {
              clearTimeout(t);
              setIsHover(false);
            };


            return (
              <div className={styles.container} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
                <div
                  className={cls(styles.poster, posterClass)}
                  style={{
                    backgroundImage: `url(${itemData.poster})`,
                  }}
                >
                  <div className={styles.topIcon}>Top 1</div>
                  {isHover ? (
                    <div className={cls(styles.player, playerClass)}>
                      <VideoPlayerCommon
                        src={itemData.src || ''}
                        poster={itemData.poster || ''}
                        triggerEvent={'hover'}
                        controls={true}
                        muted={false}
                        showFullScreen={true}
                      />
                      <!--卡片放大狀態(tài)下,底部文字區(qū)域-->
                      <div className={styles.playerBottom}>{children?.playerBottom}</div>
                    </div>
                  ) : null}
                  <div className={styles.bg}></div>
                </div>
                <!--卡片初始狀態(tài)底部文字區(qū)域-->
                <div className={styles.posterBottom}>{children?.posterBottom}</div>
              </div>
            );
          };

          export default VideoItem;

          .container {
            position: relative;

            .poster {
              position: relative;
              background-position: center;
              background-size: cover;
              background-repeat: no-repeat;

              .player {
                z-index: 10;
                position: absolute;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%) scale(0.929);
                animation: showPlayer 0.2s ease;
                animation-fill-mode: forwards;
                .playerBottom {
                  width: 100%;
                  position: absolute;
                  top: 100%;
                  left: 0;
                  box-shadow: 0px 6px 16px -8px rgba(0, 0, 0, 0.08), 0px 12px 48px 16px rgba(0, 0, 0, 0.03);
                  filter: drop-shadow(0px 9px 28px rgba(0, 0, 0, 0.05));
                }
              }
            }
            .posterBottom {
              width: 100%;
            }
          }

          @keyframes showPlayer {
            0% {
              transform: translate(-50%, -50%) scale(0.929);
            }
            100% {
              transform: translate(-50%, -50%) scale(1);
            }
          }

          2. 抽離為通用組件時(shí),卡片底部文字區(qū)域如何動(dòng)態(tài)替換

          整個(gè)卡片組件,底部的文字區(qū)域在不同的場景下可能是不同的,所以作為通用性組件,需要將這部分抽離,支持動(dòng)態(tài)替換。

          vue中最簡單的方式,就是插槽,通過插槽從外部動(dòng)態(tài)傳入。但是react框架是不支持具名插槽的。所以,這個(gè)問題就轉(zhuǎn)換成了,react如何實(shí)現(xiàn)具名插槽?

          網(wǎng)上搜到了一種實(shí)現(xiàn)方式,通過傳入一個(gè)object來實(shí)現(xiàn),具體效果如下:

          // cardList
          <VideoItem
              key={index}
              posterClass={styles.posterClass}
              playerClass={styles.playerClass}
              itemData={{
                src: item.sampleUrl,
                poster: item.coverUrl,
                industry: item.industryName,
              }}
            >
              {{
                posterBottom: bottomInfo,
                playerBottom: bottomInfo,
                onClickPlayer: () => {
                  const url = `${window.location.origin}/#/market/service/${item.itemId}`;
                  window.open(url, '_blank');
                },
              }}
          </VideoItem>


          // VideoItem

          <!--卡片放大狀態(tài)下,底部文字區(qū)域-->
          <div className={styles.playerBottom}>{props.children?.playerBottom}</div>

          <!--卡片初始狀態(tài)底部文字區(qū)域-->
          <div className={styles.posterBottom}>{props.children?.posterBottom}</div>

          當(dāng)然,具名插槽還有其它的實(shí)現(xiàn)方式,后面會(huì)專門寫一篇文章總結(jié)學(xué)習(xí)下。

          3. 列表容器如果是彈性布局時(shí),每行的列數(shù)無法固定,flex布局無法滿足

          這個(gè)問題是一個(gè)通用性問題,在容器寬度不固定時(shí),flex布局,每行最后一個(gè)元素?zé)o法選中設(shè)置樣式,同時(shí)子元素個(gè)數(shù)不固定時(shí),最后一行元素的間距會(huì)變大。

          這種情況下,就需要grid布局大顯身手了,以前很少用grid布局,這次也是學(xué)習(xí)到了,具體效果如下圖:

          在示例中,調(diào)整瀏覽器窗口大小,來實(shí)驗(yàn)彈性布局觀看效果代碼片段

          image.png

          4. 卡片放大后,可能會(huì)被容器設(shè)置的overflow:hidden給遮蓋隱藏掉

          目前放大效果的實(shí)現(xiàn)方式,放大的視頻層是絕對定位的,參照物是每個(gè)卡片本身。所以在四周邊界處的卡片,放大后,很容易被容器給遮蓋。

          處理方式也很簡單,給容器多設(shè)置一些padding,讓放大部分足夠展示,然后用margin設(shè)置負(fù)值來調(diào)整布局

          .container {
              margin-left: -20px;
              margin-right: -20px;
              padding-left: 20px;
              padding-right: 20px;
          }

          總結(jié)

          每個(gè)產(chǎn)品需求里,可能都會(huì)隱藏著自己的盲點(diǎn),將效果做到極致,就能獲得技術(shù)成長。在重復(fù)的需求里,多反思總結(jié),尋找自己的提升點(diǎn),這就是進(jìn)步吧啊。

          相關(guān)推薦

          1.CSS 實(shí)現(xiàn)按鈕點(diǎn)擊動(dòng)效的套路

          2.年份數(shù)字拼圖效果

          3.跑馬燈簡單版

          4.中心點(diǎn)靠近動(dòng)畫解析



          瀏覽 142
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久成人无码电影 | 欧美操逼免费观看 | 中文字幕区一 | 深爱激情五月天网 | 成人免费三级 |