fny5jt9 发表于 2024-9-2 12:08:57

苹果营销页的交互动画,见证CSS的魔力


    <h2 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">前言</span></h2>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">前两天在浏览 苹果 16寸 营销页面 的时候,<span style="color: black;">发掘</span>了几个比较有意思的交互,心里想着自己虽然是一个穷逼,<span style="color: black;">然则</span>知识是无界限的呀,于是便<span style="color: black;">科研</span>了一波。</p><img src="https://mmbiz.qpic.cn/mmbiz_jpg/5Xv0xlEBe99uHTPsYxaoulJRwmEKvzo9MvqHO8l2RU01uic1MMnicmrmic8GbmaqYp3Ov9CW1Wn59Z9oW2ChV0f4Q/640?wx_fmt=jpeg&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" style="width: 50%; margin-bottom: 20px;"><span style="color: black;">❝</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">文案</span><span style="color: black;">重点</span>讲交互效果,<span style="color: black;">因此</span>文中会有<span style="color: black;">非常多</span> gif 图,<span style="color: black;">大众</span>最好连上无线再看,示例代码链接我放在了<span style="color: black;">文案</span>底部,有需要自取。</p><span style="color: black;">❞</span>
    <h2 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">两个效果</span></h2>
    <h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">翻盖效果</span></h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">一个是屏幕慢慢打开的效果,在屏幕打开的过程中,<strong style="color: blue;">「电脑<span style="color: black;">照片</span>」</strong> 是在屏幕中固定不动的,直到打开完毕<span style="color: black;">或</span>关闭完毕的时候再让 <strong style="color: blue;">「电脑<span style="color: black;">照片</span>」</strong> 随着滚动条滚动。</p><img src="https://mmbiz.qpic.cn/mmbiz_gif/5Xv0xlEBe99uHTPsYxaoulJRwmEKvzo9aXvNDhnbas1KnC0GwmZ5beicviaLiaeltQfCcnP7cJmFFhLCWkObicSialg/640?wx_fmt=gif&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1" style="width: 50%; margin-bottom: 20px;">
    <h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">缩放<span style="color: black;">照片</span></span></h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">起始</span>时是一张全屏的<span style="color: black;">照片</span>,在滚动过程中慢慢变成另一张<span style="color: black;">照片</span>,接着这张<span style="color: black;">照片</span>以屏幕正中间为基准点慢慢缩小,在缩小的过程中,这张图是定在屏幕中央的,缩小到<span style="color: black;">必定</span>值的时候,<span style="color: black;">照片</span>随着滚动条滚动。</p><img src="https://mmbiz.qpic.cn/mmbiz_gif/5Xv0xlEBe99uHTPsYxaoulJRwmEKvzo9dTq0sqonEEpZZX8br5GsPowZ8cpC22MHxYsMHaD28KYJ4jU6WLrFgA/640?wx_fmt=gif&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1" style="width: 50%; margin-bottom: 20px;">
    <h2 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">前置知识</span></h2>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">再动手写代码之前,<span style="color: black;">咱们</span>需要<span style="color: black;">认识</span>几个在接下来代码中要用到的知识点。</p>
    <h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">粘性定位 sticky</span></h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">能够</span>简单的认为是 <strong style="color: blue;">「相对定位 relative」</strong> 和 <strong style="color: blue;">「固定定位 fixed」</strong> 的混合,元素在跨越指定范围前为相对定位,之后为固定定位。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">sticky 元素固定的相对偏移是相<span style="color: black;">针对</span>离它<span style="color: black;">近期</span>的<span style="color: black;">拥有</span>滚动框的祖先元素,<span style="color: black;">倘若</span>祖先元素都不<span style="color: black;">能够</span>滚动,<span style="color: black;">那样</span>是相<span style="color: black;">针对</span> viewport 来计算元素的偏移量。</p><span style="color: black;">一个例子</span><img src="https://mmbiz.qpic.cn/mmbiz_gif/5Xv0xlEBe99uHTPsYxaoulJRwmEKvzo9ibliciaer5aYY4Qk8OWthhUeibGZVeicT2KwXt9qMdV2r1ub6zNGIgt0DCQ/640?wx_fmt=gif&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1" style="width: 50%; margin-bottom: 20px;">
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">如下代码,html 结构如下:</p><span style="color: black;">&lt;body&gt;</span><span style="color: black;"> &lt;h1&gt;我是 sticky 的<span style="color: black;">第1</span>个 demo&lt;/h1&gt;</span><span style="color: black;"> &lt;nav&gt;</span><span style="color: black;"> &lt;h3&gt;导航A&lt;/h3&gt;</span><span style="color: black;"> &lt;h3&gt;导航B&lt;/h3&gt;</span><span style="color: black;"> &lt;h3&gt;导航C&lt;/h3&gt;</span><span style="color: black;"> &lt;/nav&gt;</span><span style="color: black;"> &lt;article&gt;</span><span style="color: black;">&lt;p&gt;...&lt;/p&gt;</span><span style="color: black;"> &lt;p&gt;...&lt;/p&gt;</span><span style="color: black;"> // ...</span><span style="color: black;"> &lt;/article&gt;</span><span style="color: black;">&lt;/body&gt;</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">样式如下:</p><span style="color: black;">nav {</span><span style="color: black;"> display: table;</span><span style="color: black;"> width: 100%;</span><span style="color: black;"> position: sticky;</span><span style="color: black;"> top: 0;</span><span style="color: black;">}</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">在代码中 nav 元素会<span style="color: black;">按照</span> body 进行粘性定位,在 viewport 视口滚动到元素 top 距离<span style="color: black;">少于</span> 0px 之前,元素为相对定位,<span style="color: black;">亦</span><span style="color: black;">便是</span>说他会随着文档滚动。之后,元素将固定在与顶部距离 0px 的位置。</p><span style="color: black;">原理</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">sticky 原理<span style="color: black;">大众</span><span style="color: black;">能够</span>看一下张鑫旭老师的 深入理解position sticky粘性定位的计算规则,<span style="color: black;">能够</span>先简单看一下老师讲解 sticky 时用的这个图:</p><img src="https://mmbiz.qpic.cn/mmbiz_png/5Xv0xlEBe99uHTPsYxaoulJRwmEKvzo919pJcrOw21ChzYduscFn108dKOh9SBm4pFQevibLFJ5YVsnCk8dVXSw/640?wx_fmt=png&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" style="width: 50%; margin-bottom: 20px;"><span style="color: black;">照片</span>引自 张鑫旭 深入理解position sticky<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">其中 &lt;nav&gt; 是 sticky 元素,蓝色框区域是 sticky 的爸爸元素,用于承载 sticky 元素,红色区域是 &lt;nav&gt; 相对的<span style="color: black;">能够</span>滚动的元素。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">当<span style="color: black;">全部</span>蓝色区域在红色区域中的时候,sticky 元素是<span style="color: black;">无</span>粘性效果的(如图一);</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">当慢慢的向上滑的时候,蓝色的盒子超过了红色的滚动元素,<span style="color: black;">那样</span> sticky 元素就会在蓝色的框中向下滑,实现粘性效果(如图<span style="color: black;">2、</span>三);</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">当蓝色的盒子划出红色的盒子的时候,<span style="color: black;">由于</span> sticky 元素在蓝色的框子中,<span style="color: black;">因此</span><span style="color: black;">亦</span>就直接被一波带走了,<span style="color: black;">无</span>粘性效果(如图三)。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">其实<span style="color: black;">这般</span><span style="color: black;">咱们</span>就<span style="color: black;">能够</span>很清楚的<span style="color: black;">晓得</span><span style="color: black;">为何</span> sticky 元素的高度<span style="color: black;">为何</span>不能等于它爸爸的高度了,<span style="color: black;">由于</span><span style="color: black;">倘若</span>相等的话,粘性定位元素<span style="color: black;">已然</span>完全<span style="color: black;">无</span>了实现粘性效果的空间,<span style="color: black;">亦</span>就相当于失效了。</p><span style="color: black;">❝</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">以上原理参考了张鑫旭老师的 深入理解position sticky粘性定位的计算规则,<span style="color: black;">文案</span>中有讲解 <strong style="color: blue;">「流盒」</strong> 和 <strong style="color: blue;">「粘性约束矩形」</strong> 的概念解释,以及<span style="color: black;">详细</span>的代码结构和 css 实现,<span style="color: black;">大众</span><span style="color: black;">能够</span>查看原文。</p><span style="color: black;">❞</span><span style="color: black;">常用例子</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">在业务中<span style="color: black;">咱们</span>可能会遇到<span style="color: black;">这般</span>一种场景:即一个列表,列表中的数据需要<span style="color: black;">按照</span>时间<span style="color: black;">表示</span>,<span style="color: black;">况且</span>时间需要在滚动的时候固定在最顶部,这个时候<span style="color: black;">咱们</span>就<span style="color: black;">能够</span><span style="color: black;">运用</span> sticky 来<span style="color: black;">处理</span>这个问题:</p><img src="https://mmbiz.qpic.cn/mmbiz_gif/5Xv0xlEBe99uHTPsYxaoulJRwmEKvzo9BBxZUCUMjqEwSaC9ex4mkq6vKV9ibsgHmkN4pkZhqt0kibgTEcSByktw/640?wx_fmt=gif&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1" style="width: 50%; margin-bottom: 20px;">
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">详细</span> html 结构如下:</p><span style="color: black;">&lt;body&gt;</span><span style="color: black;"> &lt;h1&gt;时间固定demo&lt;/h1&gt;</span><span style="color: black;">&lt;div className={styles.wrapper}&gt;</span><span style="color: black;"> &lt;section&gt;</span><span style="color: black;"> &lt;h4&gt;5月20日&lt;/h4&gt;</span><span style="color: black;"> &lt;ul&gt;</span><span style="color: black;"> &lt;li&gt;1&lt;/li&gt;</span><span style="color: black;"> &lt;li&gt;2&lt;/li&gt;</span><span style="color: black;"> &lt;li&gt;3&lt;/li&gt;</span><span style="color: black;"> &lt;li&gt;4&lt;/li&gt;</span><span style="color: black;"> &lt;/ul&gt;</span><span style="color: black;"> &lt;/section&gt;</span><span style="color: black;"> &lt;section&gt;</span><span style="color: black;"> &lt;h4&gt;5月19日&lt;/h4&gt;</span><span style="color: black;"> &lt;ul&gt;</span><span style="color: black;"> &lt;li&gt;1&lt;/li&gt;</span><span style="color: black;"> &lt;li&gt;2&lt;/li&gt;</span><span style="color: black;"> &lt;li&gt;3&lt;/li&gt;</span><span style="color: black;">&lt;/ul&gt;</span><span style="color: black;"> &lt;/section&gt;</span><span style="color: black;"> // ...</span><span style="color: black;">&lt;/body&gt;</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">样式如下:</p><span style="color: black;">body {</span><span style="color: black;"> margin: 0px;</span><span style="color: black;"> padding: 100px;</span><span style="color: black;"> height: 2000px;</span><span style="color: black;">}</span><span style="color: black;">h4 {</span><span style="color: black;"> margin: 2em 0 0;</span><span style="color: black;"> bac<span style="color: black;">公斤</span>round-color: #333;</span><span style="color: black;"> color: #fff;</span><span style="color: black;"> padding: 10px;</span><span style="color: black;">top: 0;</span><span style="color: black;"> z-index: 1;</span><span style="color: black;"> position: sticky;</span><span style="color: black;">}</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">代码如上,其中每<span style="color: black;">一起</span>是一个 &lt;section&gt;,<span style="color: black;">而后</span>给 &lt;h4&gt; 设置 sticky 定位,<span style="color: black;">这般</span>就<span style="color: black;">能够</span>实现<span style="color: black;">以上</span>效果了。</p><span style="color: black;"><span style="color: black;">重视</span>点</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">当然<span style="color: black;">运用</span> sticky 的时候,<span style="color: black;">咱们</span>需要<span style="color: black;">重视</span>几个点:</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">父级元素<span style="color: black;">不可</span>有任何 overflow:visible 以外的 overflow 设置,否则<span style="color: black;">无</span>粘滞效果。<span style="color: black;">倘若</span>你设置的 sticky <span style="color: black;">无</span>效果,<span style="color: black;">能够</span><span style="color: black;">瞧瞧</span>父级元素们有<span style="color: black;">无</span>设置 overflow:hidden,去掉就<span style="color: black;">能够</span>了。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">必须指定 top、bottom、left、right 4 个值之一,否则只会<span style="color: black;">处在</span>相对定位。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">父元素的高度<span style="color: black;">不可</span><span style="color: black;">小于</span> sticky 元素的高度(参考上面原理解释)</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">sticky 元素仅在其父元素内生效(参考上面原理解释)</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">还有一个不得不提的<span style="color: black;">便是</span>兼容性,<span style="color: black;">咱们</span><span style="color: black;">能够</span>在 Can I use 官网<span style="color: black;">瞧瞧</span> sticky 的兼容性,一片红:</p><img src="https://mmbiz.qpic.cn/mmbiz_png/5Xv0xlEBe99uHTPsYxaoulJRwmEKvzo9ysTnXt2MiboE2llv6a7UJ5vu5Efl39YDY1EVt47IKZNevLqmqxM1p5Q/640?wx_fmt=png&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" style="width: 50%; margin-bottom: 20px;">
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">在 IE 下完全是废了,<span style="color: black;">倘若</span>你的项目需要<span style="color: black;">思虑</span> IE 的话,你就需要<span style="color: black;">运用</span> fixed 来兼容了。</p>
    <h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">滚动视差 bac<span style="color: black;">公斤</span>round-attachment</span></h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">什么是滚动视差,来看一下下面这个例子就明白了:</p><img src="https://mmbiz.qpic.cn/mmbiz_gif/5Xv0xlEBe99uHTPsYxaoulJRwmEKvzo9PpoSFGy2R5QYCgrRicvicKs4nwq1trHoe44ZVDgriczMSvPleg0gXdiatg/640?wx_fmt=gif&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1" style="width: 50%; margin-bottom: 20px;">
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">「视差滚动」</strong>(Parallax Scrolling)<span style="color: black;">指的是</span>让多层背景以<span style="color: black;">区别</span>的速度移动,形成立体的运动效果,带来非常出色的视觉体验。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">上图中的效果,<span style="color: black;">咱们</span>只需要一行 css 就<span style="color: black;">能够</span>实现了,不需要写<span style="color: black;">繁杂</span>的 js 代码,直接设置 bac<span style="color: black;">公斤</span>round-attachment: fixed 就完<span style="color: black;">成为了</span>。</p><span style="color: black;">html 结构</span><span style="color: black;">&lt;body&gt;</span><span style="color: black;"> &lt;section className={`${styles.gImg} ${styles.gImg1}`}&gt;IMG1&lt;/section&gt;</span><span style="color: black;">&lt;section className={`${styles.gImg} ${styles.gImg2}`}&gt;IMG2&lt;/section&gt;</span><span style="color: black;"> &lt;section className={`${styles.gImg} ${styles.gImg3}`}&gt;IMG3&lt;/section&gt;</span><span style="color: black;">&lt;/body&gt;</span><span style="color: black;">样式代码</span><span style="color: black;">section {</span><span style="color: black;"> height: 100vh;</span><span style="color: black;">}</span><span style="color: black;">.gImg {</span><span style="color: black;">bac<span style="color: black;">公斤</span>round-attachment: fixed;</span><span style="color: black;"> bac<span style="color: black;">公斤</span>round-size: cover;</span><span style="color: black;"> bac<span style="color: black;">公斤</span>round-position: center center;</span><span style="color: black;"> width: 100%;</span><span style="color: black;">}</span><span style="color: black;">.gImg1 {</span><span style="color: black;"> bac<span style="color: black;">公斤</span>round-image: url(@/assets/mac1.jpg);</span><span style="color: black;">}</span><span style="color: black;">.gImg2 {</span><span style="color: black;">bac<span style="color: black;">公斤</span>round-image: url(@/assets/mac2.jpg);</span><span style="color: black;">}</span><span style="color: black;">.gImg3 {</span><span style="color: black;"> bac<span style="color: black;">公斤</span>round-image: url(@/assets/mac4.jpg);</span><span style="color: black;">}</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">经过</span>滚动视差这个 css <span style="color: black;">咱们</span>基本上<span style="color: black;">能够</span>实现第二个动画了。</p><span style="color: black;">❝</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">关于滚动视差的讲解,<span style="color: black;">大众</span><span style="color: black;">能够</span>参考这篇<span style="color: black;">文案</span> 滚动视差?CSS 不在话下,写的很<span style="color: black;">仔细</span>。</p><span style="color: black;">❞</span>
    <h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">Canvas 画图</span></h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">其实第二个动画<span style="color: black;">咱们</span><span style="color: black;">亦</span><span style="color: black;">能够</span><span style="color: black;">运用</span> canvas 画图来实现,<span style="color: black;">咱们</span><span style="color: black;">能够</span>在<span style="color: black;">一起</span>画布中画出两张<span style="color: black;">照片</span>,<span style="color: black;">按照</span>滚动的距离,去<span style="color: black;">表示</span>两张<span style="color: black;">照片</span>在画布中的比例。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">能够</span><span style="color: black;">经过</span> canvas <span style="color: black;">供给</span>的 drawImage <span style="color: black;">办法</span>来进行画图,这个<span style="color: black;">办法</span><span style="color: black;">供给</span>了多种方式在 Canvas 上绘制图像。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">例如</span><span style="color: black;">咱们</span>需要实现的画出如下图:</p><img src="https://mmbiz.qpic.cn/mmbiz_png/5Xv0xlEBe99uHTPsYxaoulJRwmEKvzo92rQxw8bs5WCnK7jfXz8e1uZjpmO4ViaibF5Y8OB7BwQmvAS5m0ws2OMQ/640?wx_fmt=png&amp;tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" style="width: 50%; margin-bottom: 20px;">
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">其实<span style="color: black;">咱们</span>就需要截取<span style="color: black;">第1</span>章<span style="color: black;">照片</span>的上半部分,下一张<span style="color: black;">照片</span>的下半部分,<span style="color: black;">而后</span>进行拼接就 ojbk 了,<span style="color: black;">瞧瞧</span>参数解释图:</p><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;">
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">这儿</span><span style="color: black;">咱们</span>需要传入 7 个参数,来实现<span style="color: black;">咱们</span>需要的效果:</p><span style="color: black;">ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">详细</span>参数的意思笔者<span style="color: black;">亦</span>就<span style="color: black;">再也不</span><span style="color: black;">这儿</span>细讲了,<span style="color: black;">大众</span><span style="color: black;">能够</span>参考一下 drawImage() MDN 文档。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">思路大致是<span style="color: black;">首要</span>绘制<span style="color: black;">第1</span>张<span style="color: black;">照片</span>,<span style="color: black;">做为</span>底图,<span style="color: black;">而后</span><span style="color: black;">咱们</span><span style="color: black;">经过</span>绘制第二张<span style="color: black;">照片</span>,覆盖掉部分<span style="color: black;">第1</span>张<span style="color: black;">照片</span>,<span style="color: black;">这般</span>就<span style="color: black;">能够</span>实现前面<span style="color: black;">说到</span>的效果。假设<span style="color: black;">咱们</span><span style="color: black;">照片</span>的原始宽高为 2048*1024,画布的<span style="color: black;">体积</span>为 544*341,在滚动的时候的偏移距离为 offsetTop,<span style="color: black;">这般</span><span style="color: black;">咱们</span>就<span style="color: black;">能够</span>写出如下代码:</p><span style="color: black;">function drawImage() {</span><span style="color: black;"> context.drawImage(img1, 0, 0, 2048, 1024, 0, 0, 544, 341);</span><span style="color: black;">context.drawImage(img2, 0, 滚动偏移距离 * 1024 / 341, 2048, 1024, 0, 滚动偏移距离, 544, 341);</span><span style="color: black;">}</span><span style="color: black;">❝</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">之前笔者<span style="color: black;">运用</span>过 ctx.drawImage(image, dx, dy, dWidth, dHeight),<span style="color: black;">能够</span>参考笔者写的 <span style="color: black;">运用</span> React Hooks 实现仿石墨的<span style="color: black;">照片</span>预览插件,这次用到了 7 个参数,<span style="color: black;">大众</span><span style="color: black;">能够</span>参考这篇<span style="color: black;">文案</span> 将<span style="color: black;">照片</span>画到canvas 上的几种<span style="color: black;">办法</span>,写的很<span style="color: black;">仔细</span>。</p><span style="color: black;">❞</span>
    <h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">transform 中的 matrix</span></h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">CSS3 中<span style="color: black;">运用</span> transform <span style="color: black;">能够</span>对元素进行变换。其中<span style="color: black;">包括</span>:位移、旋转、偏移、缩放。transform <span style="color: black;">能够</span><span style="color: black;">运用</span> translate/rotate/skew/scale 的方式来<span style="color: black;">掌控</span>元素变换,<span style="color: black;">亦</span><span style="color: black;">能够</span><span style="color: black;">运用</span> matrix 的方式来<span style="color: black;">掌控</span>元素变换。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">举个例子:</p><span style="color: black;">// 代码一</span><span style="color: black;">transform: matrix(1.5, 0, 0, 1.5, 0, 190.5);</span><span style="color: black;">// 代码二</span><span style="color: black;">transform: scale(1,5, 1.5) translate(0, 190.5)</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">上面两行代码的意思是<span style="color: black;">同样</span>的,之后<span style="color: black;">咱们</span>做到第二个动画的时候会用到这个属性。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">倘若</span><span style="color: black;">大众</span>想要深入<span style="color: black;">认识</span>这个属性,<span style="color: black;">能够</span>参考:大学没学过数学<span style="color: black;">亦</span>要理解 CSS3 transform 中的 matrix</p>
    <h2 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">开撸</span></h2>
    <h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">初始化项目</span></h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">工欲善其事,必先利其器。笔者<span style="color: black;">运用</span> react Hooks 来完成这两个动画效果,并<span style="color: black;">运用</span> umi 快速初始化一个项目,<span style="color: black;">详细</span>的初始化<span style="color: black;">过程</span><span style="color: black;">能够</span>参考笔者写的 dva理论到实践——帮你扫清dva的知识盲点,里面<span style="color: black;">仔细</span>介绍了<span style="color: black;">怎样</span><span style="color: black;">运用</span>脚手架快速搭建一个项目。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">搭建完成之后,笔者将之前讲到的例子都放在<span style="color: black;">这儿</span>,方便<span style="color: black;">大众</span>点进去查看。</p><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;">
    <h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">翻盖效果</span></h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">翻盖效果其实很简单,你们绝对想不到,苹果营销页是怎么做的?</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">「它用了 120 张<span style="color: black;">照片</span>,<span style="color: black;">按照</span>滚动距离来画出对应的在这个滚动位置上该展示的<span style="color: black;">照片</span>」</strong>,对,你<span style="color: black;">无</span>听错。我之前<span style="color: black;">亦</span>以为应该是 css3 <span style="color: black;">掌控</span>盖的<span style="color: black;">方向</span>从而实现翻盖效果的,是我想多了,哈哈哈。</p><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"><span style="color: black;">思路</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">那样<span style="color: black;">咱们</span>实现就很简单了,<span style="color: black;">咱们</span>只需要做以下几点:</p><span style="color: black;">首要</span>要定义一个常量,规定从盖着到完全打开需要 <strong style="color: blue;">「滚动多少距离」</strong>来完成,<span style="color: black;">咱们</span><span style="color: black;">这儿</span>定义为 400px。<span style="color: black;">咱们</span>需要<span style="color: black;">晓得</span>什么时候<span style="color: black;">起始</span>进行翻盖<span style="color: black;">或</span>合盖操作,这个<span style="color: black;">能够</span>让<span style="color: black;">照片</span>在屏幕正中间的时候,让其<span style="color: black;">起始</span>动画。<span style="color: black;">// <span style="color: black;">起始</span>动画的 scrollTop</span><span style="color: black;">// $(#imgWrapper) 放<span style="color: black;">照片</span>的容器,html 结构下面有</span><span style="color: black;">startOpen = $(#imgWrapper).offset().top - (window.innerHeight / 2 - $(#imgWrapper).height() / 2);</span>当翻盖<span style="color: black;">或</span>合盖的时候,<span style="color: black;">咱们</span>需要将电脑固定在视口中,等到完全翻开<span style="color: black;">或</span>合上的时候,再让起随滚动条滚动,<span style="color: black;">这儿</span><span style="color: black;">咱们</span><span style="color: black;">能够</span><span style="color: black;">运用</span> position: sticky。<span style="color: black;">html 结构</span><span style="color: black;">&lt;body&gt;</span><span style="color: black;"> // ...</span><span style="color: black;">&lt;div className={styles.stickyContainer}&gt;</span><span style="color: black;"> &lt;div className={styles.stickyWrapper}&gt;</span><span style="color: black;"> &lt;div id="imgWrapper" className={styles.imgWrapper}&gt;</span><span style="color: black;"> &lt;img</span><span style="color: black;">src={require(`@/assets/${asset}.jpg`)}</span><span style="color: black;"> alt="<span style="color: black;">照片</span>1"</span><span style="color: black;"> /&gt;</span><span style="color: black;"> &lt;/div&gt;</span><span style="color: black;"> &lt;/div&gt;</span><span style="color: black;"> &lt;/div&gt;</span><span style="color: black;"> // ...</span><span style="color: black;">&lt;/body&gt;</span><span style="color: black;">❝</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">其中动态引入<span style="color: black;">照片</span><span style="color: black;">咱们</span><span style="color: black;">能够</span><span style="color: black;">经过</span> require(<span style="color: black;">照片</span>路径) 来完成,如上面的代码,<span style="color: black;">咱们</span>只需要计算出对应滚动距离所需要展示的<span style="color: black;">照片</span>名字<span style="color: black;">就可</span>。</p><span style="color: black;">❞</span><span style="color: black;">样式代码</span><span style="color: black;">.stickyContainer {</span><span style="color: black;">height: 150vh;</span><span style="color: black;">}</span><span style="color: black;">.stickyWrapper {</span><span style="color: black;"> height: 100vh;</span><span style="color: black;"> position: sticky;</span><span style="color: black;"> top: 100px;</span><span style="color: black;">}</span><span style="color: black;">.imgWrapper {</span><span style="color: black;"> width: 100vh;</span><span style="color: black;"> height: 521px;</span><span style="color: black;"> margin: 0 auto;</span><span style="color: black;">}</span><span style="color: black;">.imgWrapper img {</span><span style="color: black;"> width: 100%;</span><span style="color: black;">}</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">接着<span style="color: black;">便是</span>在滚动的过程中计算出当先需要<span style="color: black;">表示</span>的<span style="color: black;">照片</span>是那一张,<span style="color: black;">咱们</span>上面<span style="color: black;">说到</span>:120 张<span style="color: black;">照片</span>,在 400px 的滚动距离中完成动画。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">首要</span><span style="color: black;">咱们</span>再加载完成后<span style="color: black;">能够</span>得出,<span style="color: black;">咱们</span><span style="color: black;">能够</span>得出<span style="color: black;">起始</span>动画的距离文档顶部的滚动值 startOpen,<span style="color: black;">因此呢</span><span style="color: black;">咱们</span><span style="color: black;">能够</span>得出如下代码:</p><span style="color: black;">useEffect(() =&gt; {</span><span style="color: black;"> // 绑定事件</span><span style="color: black;">window.addEventListener(scroll, scrollEvent, false);</span><span style="color: black;"> // <span style="color: black;">起始</span>动画的滚动距离</span><span style="color: black;"> // startOpen</span><span style="color: black;">startOpen = $(#imgWrapper).offset().top - (window.innerHeight / 2 - $(#imgWrapper).height() / 2);</span><span style="color: black;"> return ()=&gt;{</span><span style="color: black;"> window.removeEventListener(scroll, scrollEvent, false);</span><span style="color: black;"> }</span><span style="color: black;">}, []);</span><span style="color: black;">// 滚动事件</span><span style="color: black;">const scrollEvent = () =&gt; {</span><span style="color: black;"> // 实时的 scrollTop</span><span style="color: black;">const scrollTop = $(html).scrollTop();</span><span style="color: black;"> let newAsset = </span><span style="color: black;"> if (scrollTop &gt; startOpen &amp;&amp; scrollTop &lt; startOpen + 400) {</span><span style="color: black;">let offset = Math.floor((scrollTop - startOpen) / 400 * 120);</span><span style="color: black;"> if (offset &lt; 1) {</span><span style="color: black;"> offset = 1;</span><span style="color: black;"> } else if (offset &gt; 120) {</span><span style="color: black;"> offset = 120;</span><span style="color: black;"> }</span><span style="color: black;"> if (offset &lt; 10) {</span><span style="color: black;"> newAsset = `large_000${offset}`;</span><span style="color: black;">} else if (offset &lt; 100) {</span><span style="color: black;"> newAsset = `large_00${offset}`;</span><span style="color: black;"> } else {</span><span style="color: black;"> newAsset = `large_0${offset}`;</span><span style="color: black;"> }</span><span style="color: black;"> }</span><span style="color: black;"> // 边界值判断</span><span style="color: black;"> // ....</span><span style="color: black;"> // 设置<span style="color: black;">照片</span> url</span><span style="color: black;"> setAsset(newAsset);</span><span style="color: black;">};</span><span style="color: black;">预览效果</span><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"><span style="color: black;">❝</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">这个翻盖动画很简单,120张<span style="color: black;">照片</span>换着来,实时渲染对应的<span style="color: black;">照片</span>,其实<span style="color: black;">无</span>什么技术含量,<span style="color: black;">大众</span><span style="color: black;">亦</span><span style="color: black;">能够</span>尝试一下用其他的<span style="color: black;">办法</span>实现一波。</p><span style="color: black;">❞</span>
    <h3 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">缩放<span style="color: black;">照片</span></span></h3>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">缩放<span style="color: black;">照片</span>到屏幕这个动画<span style="color: black;">咱们</span><span style="color: black;">能够</span>用两个方式实现,一个是 <strong style="color: blue;">「滚动视差」</strong> 实现,一个是 canvas 在滚动过程中实时渲染<span style="color: black;">照片</span>。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">起始</span>之前<span style="color: black;">咱们</span>来看一下<span style="color: black;">无</span>放大的之前图,如下:</p><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;">
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">它由两张<span style="color: black;">照片</span><span style="color: black;">构成</span>,屏幕中<span style="color: black;">表示</span>的<span style="color: black;">照片</span>,他与 <span style="color: black;">电脑外壳</span> 的上间距是 18px,当放大了之后,<span style="color: black;">照片</span>与<span style="color: black;">电脑外壳<span style="color: black;">照片</span></span> 的上边距应该是 18 * 放大比率。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">电脑外壳<span style="color: black;">照片</span></span>,如下:</p><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;">
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">接下来<span style="color: black;">咱们</span>就<span style="color: black;">起始</span>介绍两种实现方式。</p><span style="color: black;">Canvas 实现</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">Canvas 实现是将屏幕中<span style="color: black;">表示</span>的这张<span style="color: black;">照片</span>由 Canvas 来画。</p>思路<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">其实这个动画有两部分<span style="color: black;">构成</span>,一个是 <strong style="color: blue;">「<span style="color: black;">照片</span>覆盖」</strong>,一个是 <strong style="color: blue;">「<span style="color: black;">照片</span>缩小」</strong>。</p><span style="color: black;">照片</span>覆盖<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">运用</span> Canvas 来<span style="color: black;">处理</span>,<span style="color: black;">运用</span> Canvas 实现<span style="color: black;">咱们</span>需要<span style="color: black;">运用</span> drawImage <span style="color: black;">办法</span>将两张<span style="color: black;">照片</span>画到一张画布上。只需要<span style="color: black;">经过</span>滚动的距离,对应计算出<span style="color: black;">详细</span>某个时候画布应该画多少比例的<span style="color: black;">第1</span>张图,画多少比例的第二张图,就<span style="color: black;">能够</span><span style="color: black;">处理</span>了。只需要<span style="color: black;">晓得</span>什么时候<span style="color: black;">起始</span><span style="color: black;">照片</span>覆盖。</p><span style="color: black;">照片</span>缩小<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">咱们</span><span style="color: black;">运用</span> transform: matrix 来实现,其中<span style="color: black;">照片</span>缩小是基于屏幕正中央的点进行缩放的。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">咱们</span><span style="color: black;">按照</span>滚动的距离相应的计算出相应放大比率和 translate 的值,如下图,实时改变 transform: matrix 的参数值就行了。</p><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;">
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">这儿</span><span style="color: black;">咱们</span>需要计算出几个临界点的值,<span style="color: black;">例如</span>最大/小的放大比率,最大/小偏移值,<span style="color: black;">起始</span>缩小的点等。</p><strong style="color: blue;">「在进行动画的时候,canvas 包裹容器应该是 sticky 定位在视口中的,直到动画结束,canvas 包裹容器才会随着滚动条滚动。」</strong><span style="color: black;">有些</span>重要的值<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">这儿</span><span style="color: black;">咱们</span>需要<span style="color: black;">晓得</span>几个值:</p>定义的常量<span style="color: black;">// canvas <span style="color: black;">表示</span>的<span style="color: black;">照片</span>宽度</span><span style="color: black;">const CANVAS_WIDTH = 544;</span><span style="color: black;">// canvas <span style="color: black;">表示</span>的<span style="color: black;">照片</span>高度</span><span style="color: black;">const CANVAS_HEIGHT = 341;</span><span style="color: black;">// 动画<span style="color: black;">连续</span>的距离</span><span style="color: black;">const ZOOM_SCROLL_RANGE = 400;</span><span style="color: black;">// canvas <span style="color: black;">表示</span>的<span style="color: black;">照片</span> <span style="color: black;">实质</span>宽度</span><span style="color: black;">const IMG_NATURAL_WIDTH = 2048;</span><span style="color: black;">// canvas <span style="color: black;">表示</span>的<span style="color: black;">照片</span> <span style="color: black;">实质</span>高度</span><span style="color: black;">const IMG_NATURAL_HEIGHT = 1024;</span>放大比率(curScale),用于 matrix 的 scale 值<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">最小的放大比率为 1,即是<span style="color: black;">自己</span>。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">最大的放大比率是屏幕的高度除以屏幕<span style="color: black;">表示</span><span style="color: black;">照片</span>的比率,<span style="color: black;">这儿</span>笔者将 canvas 画出来的<span style="color: black;">照片</span>宽高定位 544 * 341。</p><span style="color: black;">const CANVAS_WIDTH = 544;</span><span style="color: black;">const CANVAS_HEIGHT = 341;</span><span style="color: black;">const scaleRadio = window.innerHeight / CANVAS_HEIGHT;</span><span style="color: black;">❝</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">因此</span>放大比率的区间应该是 1 ~ scaleRadio 之间。</p><span style="color: black;">❞</span>偏移距离(translate),用于 matrix 的 偏移值<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">最大的偏移距离,应该是当 curScale 为 1 的时候,包裹元素距离视口顶部的距离,<span style="color: black;">咱们</span>的缩放<span style="color: black;">始终</span>都是基于屏幕正中央这个点来进行放大/缩小的,<span style="color: black;">因此</span><span style="color: black;">能够</span>很简单的得出:</p><span style="color: black;">// 最大的 translate</span><span style="color: black;">let StartScale = 0;</span><span style="color: black;">StartScale = window.innerHeight / 2 - $(#img-wrapper).height() / 2;</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">最小的偏移距离,应该是在 curScale 为 scaleRadio 时,包裹元素距离视口顶部的距离,这个时候,<span style="color: black;">咱们</span>就需要用到之前<span style="color: black;">说到</span>的视屏<span style="color: black;">照片</span>到电脑外壳的 top = 18px 这个值了,<span style="color: black;">由于</span><span style="color: black;">照片</span>进行了放大,<span style="color: black;">因此</span>最小的偏移距离应该为:</p><span style="color: black;">miniTranslate = - 18 * scaleRadio</span><span style="color: black;">❝</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">因此</span>偏移距离的区间应该是 miniTranslate ~ StartScale 之间。</p><span style="color: black;">❞</span><span style="color: black;">起始</span>缩放操作的<span style="color: black;">初始</span>点(NewStartScale)<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">其实很简单<span style="color: black;">咱们</span>需要在第二章<span style="color: black;">照片</span>完全覆盖掉<span style="color: black;">第1</span>张的<span style="color: black;">照片</span>的时候就进行<span style="color: black;">起始</span>缩放,这个值可以<span style="color: black;">经过</span><strong style="color: blue;">「Canvas 包裹元素距离顶部文档的top值」</strong> 加上 <strong style="color: blue;">「一屏的高度」</strong> 就能计算出。</p><span style="color: black;">let NewStartScale = 0;</span><span style="color: black;">NewStartScale = $(#section-sticky-hero).offset().top + window.innerHeight;</span>核心代码<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">核心代码<span style="color: black;">便是</span>滚动时候的计算:</p><span style="color: black;">const scrollEvent = () =&gt; {</span><span style="color: black;"> // 当前的 scrollTop</span><span style="color: black;"> const scrollTop = $(html).scrollTop();</span><span style="color: black;"> // 放大比率 默认为最大</span><span style="color: black;"> let curScale = scaleRadio;</span><span style="color: black;"> // 偏移距离 默认为最小</span><span style="color: black;">let translate = -scaleRadio * 18;</span><span style="color: black;"> // StartScale:最大的偏移距离</span><span style="color: black;"> // NewStartScale:<span style="color: black;">起始</span>缩放操作的<span style="color: black;">初始</span>点</span><span style="color: black;"> // <span style="color: black;">无</span>就 return</span><span style="color: black;"> if (!NewStartScale || !StartScale) return;</span><span style="color: black;"> // 计算 当前的 curScale</span><span style="color: black;">// (scaleRadio - 1) / ZOOM_SCROLL_RANGE):每 1px 放大多少</span><span style="color: black;"> // scrollTop + scaleRadio * 18 - NewStartScale:当前滚动了多少</span><span style="color: black;">curScale = scaleRadio - ((scaleRadio - 1) / ZOOM_SCROLL_RANGE) * (scrollTop + scaleRadio * 18 - NewStartScale);</span><span style="color: black;"> // 边界值处理</span><span style="color: black;"> if (curScale &gt; scaleRadio) {</span><span style="color: black;"> curScale = scaleRadio;</span><span style="color: black;"> } else if (curScale &lt; 1) {</span><span style="color: black;">curScale = 1;</span><span style="color: black;"> }</span><span style="color: black;"> // 计算 当前的 translate</span><span style="color: black;"> // 从 scaleRadio * 18 <span style="color: black;">起始</span></span><span style="color: black;"> // all = scaleRadio * 18 + StartScale</span><span style="color: black;"> // 滑动过程中<span style="color: black;">持续</span>相加</span><span style="color: black;">translate = -scaleRadio * 18 + ((scrollTop + scaleRadio * 18 - NewStartScale) / ZOOM_SCROLL_RANGE * (scaleRadio * 18 + StartScale));</span><span style="color: black;"> // 边界值处理</span><span style="color: black;"> if (translate &gt; StartScale) {</span><span style="color: black;"> translate = StartScale;</span><span style="color: black;">} else if (translate &lt; -scaleRadio * 18) {</span><span style="color: black;"> translate = - scaleRadio * 18;</span><span style="color: black;"> }</span><span style="color: black;"> // <span style="color: black;">运用</span> canvas 画图</span><span style="color: black;"> if (image1 &amp;&amp; image2) {</span><span style="color: black;"> // 在<span style="color: black;">照片</span>覆盖<span style="color: black;">周期</span></span><span style="color: black;"> // curScale 还是最大的比率</span><span style="color: black;">if (curScale === scaleRadio) {</span><span style="color: black;"> drawImage({</span><span style="color: black;"> img1: image1,</span><span style="color: black;"> img2: image2,</span><span style="color: black;"> secTop: CANVAS_HEIGHT * (scrollTop + 18 * scaleRadio - NewStartScale) / window.innerHeight,</span><span style="color: black;"> });</span><span style="color: black;"> } else {</span><span style="color: black;">// <span style="color: black;">倘若</span>不是最大的比率,说明<span style="color: black;">照片</span><span style="color: black;">已然</span>覆盖完了</span><span style="color: black;"> // 直接<span style="color: black;">表示</span>第二章</span><span style="color: black;"> drawImage({</span><span style="color: black;"> img1: image1,</span><span style="color: black;"> img2: image2,</span><span style="color: black;"> secTop: 0,</span><span style="color: black;"> });</span><span style="color: black;"> }</span><span style="color: black;"> }</span><span style="color: black;"> // 设置样式</span><span style="color: black;"> $(#img-wrapper).css({</span><span style="color: black;">transform: `matrix(${curScale}, 0, 0, ${curScale}, 0, ${translate})`,</span><span style="color: black;"> });</span><span style="color: black;">};</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">html 结构如下:</p><span style="color: black;">&lt;body&gt;</span><span style="color: black;"> // ... 其他内容</span><span style="color: black;"> &lt;div id="section-sticky-hero" className={styles.stickyContainer}&gt;</span><span style="color: black;">&lt;div className={styles.componentContainer}&gt;</span><span style="color: black;"> &lt;div className={styles.imgWrapper} id="img-wrapper"&gt;</span><span style="color: black;"> &lt;canvas ref={canvasRef} id="canvas" className={styles.canvas}&gt;&lt;/canvas&gt;</span><span style="color: black;">&lt;/div&gt;</span><span style="color: black;"> &lt;/div&gt;</span><span style="color: black;"> &lt;/div&gt;</span><span style="color: black;"> // ... 其他内容</span><span style="color: black;">&lt;/body&gt;</span><span style="color: black;">❝</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">篇幅有限,笔者只列举了滚动事件的代码和 html 结构,其他的代码,<span style="color: black;">例如</span> drawImage 这个<span style="color: black;">办法</span>,<span style="color: black;">大众</span>有兴趣的话,<span style="color: black;">能够</span>参考源码。</p><span style="color: black;">❞</span>预览效果图<img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"><span style="color: black;">滚动视差实现</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">前面<span style="color: black;">咱们</span><span style="color: black;">亦</span>讲了滚动视差的原理,有了这个 bac<span style="color: black;">公斤</span>round-attachment: fixed 属性,第二个动画基本上<span style="color: black;">已然</span>实现一半了。</p>实现思路<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">和上面的 canvas 画图相比的话,其实<span style="color: black;">便是</span><span style="color: black;">照片</span>覆盖的这一步不<span style="color: black;">同样</span>,其他基本上都是类似的,<span style="color: black;">包含</span>边界值的计算。</p><span style="color: black;">照片</span>覆盖<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">这儿</span><span style="color: black;">咱们</span>需要将两张<span style="color: black;">照片</span>都设置为背景<span style="color: black;">照片</span>,<span style="color: black;">同期</span><span style="color: black;">咱们</span>需要给第二张<span style="color: black;">照片</span>套上 <span style="color: black;">电脑外壳<span style="color: black;">照片</span></span>。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">当<span style="color: black;">第1</span>张<span style="color: black;">照片</span>充满屏幕的时候,就给两张<span style="color: black;">照片</span><span style="color: black;">同期</span>加上 bac<span style="color: black;">公斤</span>round-attachment: fixed 属性,<span style="color: black;">不可</span>一<span style="color: black;">起始</span>的时候就加上这个属性,<span style="color: black;">否则</span>就会变成下面这个效果:</p><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"><span style="color: black;">照片</span>缩小<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">这儿</span><span style="color: black;">咱们</span>不<span style="color: black;">运用</span> &nbsp;transform: matrix 来做这个放大缩小,<span style="color: black;">咱们</span><span style="color: black;">运用</span> bac<span style="color: black;">公斤</span>round-position 和 bac<span style="color: black;">公斤</span>round-size 来进行<span style="color: black;">照片</span>的<strong style="color: blue;">「缩小/放大和偏移」</strong>。</p>其他的都与 Canvas 实现的原理大同小异。核心代码<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">滚动<span style="color: black;">规律</span>代码如下:</p><span style="color: black;">const CANVAS_WIDTH = 544;</span><span style="color: black;">const CANVAS_HEIGHT = 341;</span><span style="color: black;">const WRAPPER_WIDTH = 694;</span><span style="color: black;">const WRAPPER_HEIGHT = 408;</span><span style="color: black;">const ZOOM_SCROLL_RANGE = 400;</span><span style="color: black;">// scalaRadio</span><span style="color: black;">// <span style="color: black;">照片</span>放大的最大的倍数</span><span style="color: black;">const scaleRadio = window.innerHeight / CANVAS_HEIGHT;</span><span style="color: black;">const scrollEvent = () =&gt; {</span><span style="color: black;">const scrollTop = $(html).scrollTop();</span><span style="color: black;"> let curScale = scaleRadio;</span><span style="color: black;"> let translate = -scaleRadio * 18;</span><span style="color: black;"> if (!imgFixFixed || !StartScale) return;</span><span style="color: black;">// <span style="color: black;">第1</span>张<span style="color: black;">照片</span>的 距离文档的顶部的距离为 imgFixFixed</span><span style="color: black;"> // <span style="color: black;">第1</span>章<span style="color: black;">照片</span>的高度为 100vh,即一屏的高度</span><span style="color: black;"> // <span style="color: black;">因此</span>第二章<span style="color: black;">照片</span>的 scrollTop 为 imgFixFixed + window.innerHeight</span><span style="color: black;">if (scrollTop &gt; imgFixFixed &amp;&amp; scrollTop &lt; imgFixFixed + window.innerHeight) {</span><span style="color: black;"> // 设置 fixed 属性</span><span style="color: black;"> setFixImg(true);</span><span style="color: black;"> } else {</span><span style="color: black;"> setFixImg(false);</span><span style="color: black;"> }</span><span style="color: black;"> // 假设<span style="color: black;">咱们</span>缩放的距离是 400</span><span style="color: black;"> // <span style="color: black;">那样</span><span style="color: black;">咱们</span><span style="color: black;">能够</span>计算出 每 1px 缩放的比例</span><span style="color: black;"> // 接着一这个比例乘以滚动的距离</span><span style="color: black;">curScale = scaleRadio - ((scaleRadio - 1) / ZOOM_SCROLL_RANGE) * (scrollTop - imgFixFixed - window.innerHeight);</span><span style="color: black;"> // curScale 边界值处理</span><span style="color: black;"> // ...</span><span style="color: black;"> // 从 scaleRadio * 18 <span style="color: black;">起始</span></span><span style="color: black;">// all = scaleRadio * 18 + StartScale</span><span style="color: black;"> // 滑动过程中<span style="color: black;">持续</span>相加</span><span style="color: black;">translate = -scaleRadio * 18 + ((scrollTop - imgFixFixed - window.innerHeight) / ZOOM_SCROLL_RANGE * (scaleRadio * 18 + StartScale));</span><span style="color: black;"> // translate 边界值处理</span><span style="color: black;"> // ...</span><span style="color: black;"> // 设置<span style="color: black;">照片</span>的 css 样式</span><span style="color: black;"> // 进行<span style="color: black;">照片</span>基于中心点的缩放</span><span style="color: black;"> $(#g-img2).css({</span><span style="color: black;"> "width": curScale * CANVAS_WIDTH,</span><span style="color: black;">"height": curScale * CANVAS_HEIGHT,</span><span style="color: black;"> "margin-top": `${translate + 18 * curScale}px`,</span><span style="color: black;"> });</span><span style="color: black;"> $(#img-wrapper).css({</span><span style="color: black;"> "width": scaleRadio * WRAPPER_WIDTH,</span><span style="color: black;">"height": scaleRadio * WRAPPER_HEIGHT,</span><span style="color: black;"> "bac<span style="color: black;">公斤</span>round-size": `${curScale * WRAPPER_WIDTH}px ${curScale * WRAPPER_HEIGHT}px`,</span><span style="color: black;"> "bac<span style="color: black;">公斤</span>round-position": `center ${translate}px`,</span><span style="color: black;"> });</span><span style="color: black;">};</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">html 结构如下:</p><span style="color: black;">&lt;body&gt;</span><span style="color: black;"> // ... 其他内容</span><span style="color: black;"> &lt;section id="g-img" className={`${styles.gImg} ${styles.gImg1} ${fixImg ? styles.fixed : }`}&gt;IMG1&lt;/section&gt;</span><span style="color: black;">&lt;div className={styles.stickyContainer}&gt;</span><span style="color: black;"> &lt;div className={styles.componentContainer}&gt;</span><span style="color: black;"> &lt;div className={styles.imgWrapper} id="img-wrapper"&gt;</span><span style="color: black;">&lt;section id="g-img2" className={`${styles.gImg} ${styles.gImg2} ${fixImg ? styles.fixed : }`}&gt;IMG2&lt;/section&gt;</span><span style="color: black;"> &lt;/div&gt;</span><span style="color: black;"> &lt;/div&gt;</span><span style="color: black;"> &lt;/div&gt;</span><span style="color: black;"> // ... 其他内容</span><span style="color: black;">&lt;/body&gt;</span>预览效果图<img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;">
    <h2 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">总结</span></h2>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">今天讲了两个苹果营销页面的动画,<span style="color: black;">文案</span>没什么难点,<span style="color: black;">重点</span>是对几个<span style="color: black;">基本</span>知识点的运用。<strong style="color: blue;">「粘性定位」</strong>、<strong style="color: blue;">「滚动视差」</strong>、<strong style="color: blue;">「Canvas 画图」</strong>、<strong style="color: blue;">「matrix 属性的<span style="color: black;">运用</span>」</strong> 等等,<span style="color: black;">期盼</span>对<span style="color: black;">大众</span>有所<span style="color: black;">帮忙</span>。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">「实不相瞒,想要个赞!」</strong></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><strong style="color: blue;"><span style="color: black;"><strong style="color: blue;"><span style="color: black;"><strong style="color: blue;"><span style="color: black;">亲,点这涨工资&nbsp;</span></strong></span></strong></span></strong></span><strong style="color: blue;"><span style="color: black;"><strong style="color: blue;"><span style="color: black;"><strong style="color: blue;"><span style="color: black;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></span></strong></span></strong></span></strong></p>




wrjc1hod 发表于 2024-10-2 18:40:41

我们有着相似的经历,你的感受我深有体会。
页: [1]
查看完整版本: 苹果营销页的交互动画,见证CSS的魔力