SvelteJs学习——Transition动画

过渡动画

我们可以通过优雅的过渡动画效果进入和离开DOM来吸引客户,svelte通过transition指令让这个问题变得简单起来。

1. 淡入淡出

1.1 生硬显示隐藏

正常我们想要做一个元素的显示与隐藏,可以这样

<script>
	let visible = true;
</script>
<main>
	<label for="">
		<input type="checkbox" bind:checked={visible}> // 开关控制元素是否隐藏
		visible {visible}
	</label>
	{#if visible}
		<p>Fade In And Out 看看淡入淡出效果</p>
	{/if}
</main>

但实现后我们发现,这个突然出现和突然消失的效果太过生硬了,因此我们可以设置一些动画,看起来更柔和舒服一些,所以我们可以将上述代码做如下更改

1.2 淡入淡出 <div transition:fade>..

import { fade } from 'svelte/transition' // 引入动画库中具体的动画效果
// 在使用的标签上面增加transition:[动画效果]
<p transition:fade>Fade In And Out 看看淡入淡出效果</p>

1.3 接收参数 <div transition:fly={参数}>...

transition函数可以接受参数,我们可以切换一个可以接受参数的动画效果fly。这个fly效果就是在离开的时候,根据设置,向y轴方向移动并慢慢消失,出现时从离开的位置慢慢出现

import { fly } from 'svelte/transition'
<p transition:fly={{y: 100, duration: 2000}}>Fade In And Out 看看淡入淡出效果</p>

1.4 出入动画 in:[动画效果] out:[动画效果]

刚刚我们上述没有区分进行设置,动画在in和out时是可以执行不同的动画效果的,我们可以指定其中的一个或者两个。

  • transition:[动画效果] in和out都执行这个动画
  • in:[动画效果] 在出现/进入时执行这个动画效果
  • out:[动画效果] 在消失/离开时执行这个动画效果
import { fade, fly } from 'svelte/transition'

<p in:fly={{y: 100, duration: 2000}} out:fade>Fade In And Out 看看淡入淡出效果</p>

2. 自定义CSS过渡

我们可以像官方示例一样封装一个动画效果。在动画进入时绑定了一个spin的效果,由于spin是我们自定义的效果,所以我们需要定一个spin函数,接收两个参数,node当前DOM和options配置。之后在函数中对接收到的参数进行处理,就可以得到我们想要的动画效果啦

<script>
	import { fade } from 'svelte/transition'
	import { elasticOut } from 'svelte/easing'
	let visible = true;

	function spin(node, { duration }) { // node 绑定元素
		return {
			duration,
			css: t => {
				const eased = elasticOut(t)
				return `
					transform: scale(${eased}) rotate(${eased * 1080}deg);
					color: hsl(
						${~~(t * 360)},
						${Math.min(100, 1000 - 1000 * t)}%,
						${Math.min(50, 500 - 500 * t)}%
					)
				`
			}
		}
	}
</script>
<main>
	<label for="">
		<input type="checkbox" bind:checked={visible}>
		visible
	</label>
	{#if visible}
		<div class="centered" in:spin="{{duration: 8000}}" out:fade>
			<span>transitions!</span>
		</div>
	{/if}
</main>
<style>
	.centered {
		position: absolute;
		left: 50%;
		top: 50%;
		transform: translate(-50%, -50%);
	}
	span {
		position: absolute;
		transform: translate(-50%, -50%);
		font-size: 4em;
	}
</style>
  • css函数:接收一个时间参数,返回的内容是动态的根据t来进行计算的内联样式
  • hsl()函数:使用色相、饱和度、亮度来定义颜色,这样随着时间的变化,文字的内容就会各种颜色进行变换达到一个炫酷的效果
  • ~~: 相当于Math.floor()

3. 自定义JS过渡

虽然通常我们应该尽可能多的使用CSS进行过渡,但是如果不借用JavaScript,有些效果是无法实现的。以下官网示例:逐字打印

<script>
	let visible = false

	function typewriter(node, {speed = 50}) {
		const valid = (
			node.childNodes.length === 1 && node.childNodes[0].nodeType === 3 // 只有一个子节点并且为文本节点
		);
		if(!valid) {
			throw new Error('This transition only works on elements with a single text node child')
		}
		const text = node.textContent;
		const duration = text.length * speed;

		return {
			duration,
			tick: t => {
				const i = ~~(text.length * t)
				node.textContent = text.slice(0, i)
			}
		}
	}
</script>
<main>
	<label for="">
		<input type="checkbox" bind:checked={visible}>
		visible
	</label>
	{#if visible}
		<p in:typewriter>
			The quick brown fox jumps over the lazy dog
		</p>
	{/if}
</main>

4. 过渡事件

了解过渡的开始和结束会很有用,Svelte调度监听事件,像监听其他任何DOM事件一样

  • on:introstart=fn 动画进入开始时执行该函数
  • on:introend=fn 动画进入结束时执行该函数
  • on:outrostart=fn 动画离开开始时执行该函数
  • on:outroend=fn 动画离开结束时执行该函数
<script>
	import { fly } from "svelte/transition";
	let visible = true;
	let status = 'waiting'
</script>
<main>
	<p>status: {status}</p>
	<label for="">
		<input type="checkbox" bind:checked={visible}>visible
	</label>
	{#if visible}
		<p
		 transition:fly={{ y: 188, duration: 2000}}
		 on:introstart="{() =>  status = 'intro start'}"
		 on:introend="{() => status = 'intro end'}"
		 on:outrostart="{() => status = 'outro start'}"
		 on:outroend="{() => status = 'outro end'}"
		>
			动画效果进出
		</p>
	{/if}
</main>

5. 局部过渡

无论是添加或销毁任何标签容器,过渡动画都会在标签上进行播放。单个列表项的过渡效果影响到切换整个列表,以致切换可见性时也有过渡效果。如果我们只是想在列表项新增或删除时出现过渡效果,在显示和隐藏时不使用动画效果,可以通过局部(local)过渡来实现该功能。

<script>
	import { slide } from 'svelte/transition'
	let showItems = true
	let i = 5
	let items = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten']
</script>
<main>
	<label for="">
		<input type="checkbox" name="" id="" bind:checked={showItems}> 展示列表
	</label>
	<label for="">
		<input type="range" bind:value={i} max=10>
	</label>
	{#if showItems}
		{#each items.slice(0, i) as item}
			<div transition:slide|local>
				{item}
			</div>
		{/each}
	{/if}
</main>
<style>
	div {
		padding: 0.5em 0;
		border-top: 1px solid #eee;
	}
</style>

6. 延时过渡

svelte过渡引擎其中一项特别强大的功能就是可以设置延时过渡,以便多个效果之间协调。

  • 引入函数,并定义动画函数import { quintOut } from 'svelte/easing'; import { crossfade } from 'svelte/transition' const [send, receive] = crossfade({...
  • 在标签中绑定动画效果in:receive="{{key: todo.id}}" out:send="{{key: todo.id}}"
<script>
	import { quintOut } from 'svelte/easing';
	import { crossfade } from 'svelte/transition'
	const [send, receive] = crossfade({
		duration: d => Math.sqrt(d * 200),
		fallback(node, params) {
			const style = getComputedStyle(node)
			const transform = style.transform === 'none' ? '' : style.transform

			return {
				duration: 600,
				easing: quintOut,
				css: t => `
					transform: ${transform} scale(${t});
					opacity: ${t}
				`
			}
		}
	})
	let uid = 1
	let todos = [
		{ id: uid++, done: false, description: 'write some docs' },
		{ id: uid++, done: false, description: 'start writing blog post' },
		{ id: uid++, done: true,  description: 'buy some milk' },
		{ id: uid++, done: false, description: 'mow the lawn' },
		{ id: uid++, done: false, description: 'feed the turtle' },
		{ id: uid++, done: false, description: 'fix some bugs' },
	];
	function add(input) {
		const todo = {
			id: uid++,
			done: false,
			description: input.value
		}
		todos = [todo, ...todos]
		input.value = ''
	}
	function mark(todo, done) {
		todo.done = done
		remove(todo)
		todos = todos.concat(todo)
	}
	function remove(todo) {
		todos = todos.filter(t => t !== todo)
	}
</script>
<main>
	<div class="board">
		<input type="text" placeholder="请输入你的待办事项" on:keydown={e => e.which === 13 && add(e.target)}>
		<div class="left">
			<h2>待办项</h2>
			{#each todos.filter(t => !t.done) as todo (todo.id)}
				<label for="" in:receive="{{key: todo.id}}" out:send="{{key: todo.id}}">
					<input type="checkbox" on:change={() => mark(todo, true)}>
					{todo.description}
					<button on:click="{() => remove(todo)}">remove</button>
				</label>
			{/each}
		</div>
		<div class="right">
			<h2>已办</h2>
			{#each todos.filter(t => t.done) as todo (todo.id)}
				<label for="" class="done" in:receive="{{key: todo.id}}" out:send="{{key: todo.id}}">
					<input type="checkbox" checked on:change={() => mark(todo, false)}>
					{todo.description}
					<button on:click="{() => remove(todo)}">remove</button>
				</label>
			{/each}
		</div>
	</div>
</main>
<style>
.board {
		display: grid;
		grid-template-columns: 1fr 1fr;
		grid-gap: 1em;
		max-width: 36em;
		margin: 0 auto;
	}

	.board > input {
		font-size: 1.4em;
		grid-column: 1/3;
	}

	h2 {
		font-size: 2em;
		font-weight: 200;
		user-select: none;
		margin: 0 0 0.5em 0;
	}

	label {
		position: relative;
		line-height: 1.2;
		padding: 0.5em 2.5em 0.5em 2em;
		margin: 0 0 0.5em 0;
		border-radius: 2px;
		user-select: none;
		border: 1px solid hsl(240, 8%, 70%);
		background-color:hsl(240, 8%, 93%);
		color: #333;
	}

	input[type="checkbox"] {
		position: absolute;
		left: 0.5em;
		top: 0.6em;
		margin: 0;
	}

	.done {
		border: 1px solid hsl(240, 8%, 90%);
		background-color:hsl(240, 8%, 98%);
	}

	button {
		position: absolute;
		top: 0;
		right: 0.2em;
		width: 2em;
		height: 100%;
		background: no-repeat 50% 50% url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23676778' d='M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M17,7H14.5L13.5,6H10.5L9.5,7H7V9H17V7M9,18H15A1,1 0 0,0 16,17V10H8V17A1,1 0 0,0 9,18Z'%3E%3C/path%3E%3C/svg%3E");
		background-size: 1.4em 1.4em;
		border: none;
		opacity: 0;
		transition: opacity 0.2s;
		text-indent: -9999px;
		cursor: pointer;
	}

	label:hover button {
		opacity: 1;
	}
</style>

7. 示例中新知识

7.1 左移运算符 <<

  • 2 << 3
  • 左边的2转成二进制 10
  • 左移3位后变为 10000
  • 转成十进制是16

7.2 user-select 是否能选取元素的文本

在web浏览器中,如果您在文本上双击或者鼠标左键滑动,文本会被选取或高亮显示。user-select属性用于阻止这种行为。

  • user-select:none; CSS写法
  • object.style.userSelect=“none”; JS写法
  • user-select可选值:
    • auto: 默认。如果浏览器允许,则可以选择文本
    • none:防止文本可被用户选取
    • text:文本可被用户选取
    • all:单击选取文本,而不是双击

7.3 text-indent

首行文本缩进

7.4 Math.sqrt

返回一个数的平方根

  • Math.sqrt(4) -> 2

7.5 css中hsl函数

HSL 即色相、饱和度、亮度(英语:Hue, Saturation, Lightness)。

  • 色相(H)是色彩的基本属性,就是平常所说的颜色名称,如红色、黄色等。 定义色相 (0 到 360) - 0 (或 360) 为红色, 120 为绿色, 240 为蓝色
  • 饱和度(S)是指色彩的纯度,越高色彩越纯,低则逐渐变灰,取 0-100% 的数值。定义饱和度; 0% 为灰色, 100% 全色
  • 亮度(L),取 0-100%,增加亮度,颜色会向白色变化;减少亮度,颜色会向黑色变化。定义亮度 0% 为暗, 50% 为普通, 100% 为白

7.6 display: grid;网格布局

7.6.1 网格布局简介

CSS Grid 网格布局教程

  • Grid布局将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样的布局。与Flex布局有一定的相似性,都是可以指定容器内部多个项目的位置。但是它们之间也存在差异。

  • Flex布局是轴线布局,只能指定"项目"针对轴线的位置,可以看作是一维布局。Grid布局则是将容器划分成行和列,产生单元格,然后指定项目所在的单元格,可以看作是二维布局。Grid布局远比Flex布局强大。

  • display: grid; // 指定容器采用网格布局

  • 默认情况下容器元素都是块级圆度,也可以设成行内元素

  • display: inline-grid;

  • 设为网格布局以后,容器子元素(项目)的float display:inline-block display:table-cell vertical-aligncolumb-*等设置都将失效。

  • 容器,网格的最外层元素。项目,容器的顶级子元素

7.6.2 容器属性-列宽行高grid-template-columns/grid-template-rows

容器指定了网格布局以后,接着就要划分行和列

  • grid-template-columns: 100px 200px 80px 20px; // 每一列的列宽,同时容器被自动划分成对应数据个数列。
  • grid-template-rows: grid-template-rows: 60px 80px 60px; // 每一行的行高,若共计四行,只设置三个行高值,则第四个元素默认初始行高。
  • 上述列宽和行高,除了使用绝对单位px,也可使用百分比方式。
7.6.2.1 设置行高列宽时可用函数
  • repeat(): 有时重复写同样的值非常麻烦,尤其是网格较多时,此时可使用repeat函数,简化重复某个值或某种模式(重复多个值)。
    • repeat(num, a b c …) 将后面a b c设置的值重复num次
    • grid-template-columns: repeat(4, 100px 200px); // 八列,交替100 200
    • grid-template-rows: repeat(2, 33%); // 两行,分别占总高的33%
  • auto-fill: 有时单元格的大小是固定的,但是容器的大小不确定。如果希望每一行或每一列容纳尽可能多的单元格,此时可使用auto-fill关键词表示自动填充。
    • grid-template-columns: repeat(auto-fill, 100px); 均匀单元格,一行尽可能放更多的列
  • fr 片段:用于表示比例关系,如果两列的宽度分别为1fr和2fr,就表示后者是前者的两倍。
    • grid-template-columns: 1fr 2fr 3fr 1fr; // 将一行分开,四列按比例排放
    • grid-template-columns: 1fr 800px 3fr 1fr; // 将一行分开,第二列800宽,剩余宽度按比例排放
  • minmax(): 产生一个长度范围,表示长度就在这个范围之中。
    • grid-template-columns: 1fr 1fr minmax(100px, 2fr) 3fr; // 表示该列最小宽度100px,最大宽度 2fr
  • auto: 表示由浏览器自己决定长度
    • grid-template-columns: 100px auto 200px; // 除非设置min-width大于最大宽度,否则第二列基本等于该单元格额最大宽度。

7.6.2.2 网格线的名称

grid-template-columns 属性和 grid-template-rows属性里面还可以使用方括号,指定每一根网格线的名字,方便后续的引用

grid-template-columns: [c1] 100px [c2] 120px [c3] auto [c4];
grid-template-rows: [r1] 100px [r2] 100px [r3] auto [r4];

上述代码指定网格布局为3行 X 3列,所以有4根垂直网格线和4根水平网格线。[]内为这八根线的名字。网格布局中同一根线可以有很多个名字,比如[fifth-line row5]

7.6.3 容器属性-行/列间距 row-gap/column-gap/gap

  • grid-row-gap: 10px; // 行间距 行与行之间存在,不像margin,需要将last-child设为0
  • grid-column-gap: 8px; // 列间距 列与列之间存在,同上行
  • grid-gap: 10px 20px; // 行间距和列间距合并。前面行间距 后面列间距。若省略了第二个值,则默认第二个值等于第一个值。

根据最新标准,上面三个属性名的grid-前缀已经删除,grid-column-gap和grid-row-gap写成column-gap和row-gap,grid-gap写成gap。

7.6.4 容器属性-grid-template-area定义区域

网格布局允许指定“区域”,一个区域由单个或多个单元格组成。这个区域可以配合class属性、grid-area一起指定某个区域应该展现什么样式

<main>
	<!-- container对应的这个div为该网格的容器,下面的每一个project为对应的项目。 -->
	<!-- 网格根据对应的行和列生成一个又一个的单元格 -->
	<div class="container">
		<div class="project a">A</div>
		<div class="project a">B</div>
		<div class="project a">C</div>
		<div class="project b">D</div>
		<div class="project b">E</div>
		<div class="project b">F</div>
		<div class="project c">G</div>
		<div class="project c">H</div>
		<div class="project c">I</div>
	</div>
</main>
<style>
.container {
	display: grid;
	grid-template-columns: repeat(3, 100px);
	grid-template-rows: repeat(4, 100px);
	grid-column-gap: 10px;
	gap: 10px 20px;
	grid-template-areas: 'a a a'
                       'b b b'
                       'c c c';
}
.a {
	grid-area: a;
	background: antiquewhite;
}
.b {
	grid-area: b;
	background: blue;
}
.c {
	grid-area: c;
	background: blueviolet;
}
.project {
	width: 100%;
	text-align: center;
	border: 1px solid #d1d1d1;
}
</style>

我们将对应的area设置成a b c可能语义化并不是很明确,也可以这样设置

grid-template-areas: "header header header"
                     "main main sidebar"
                     "footer footer footer";
grid-template-areas: 'a . c'
                     'd . f'
                     'g . i';

中间列为点,表示没有用到该单元格,或者该单元格不属于任何区域。

注意,区域的命名会影响到网格线。每个区域的起始网格线,会自动命名为区域名-start,终止网格线自动命名为区域名-end。比如,区域名为header,则起始位置的水平网格线和垂直网格线叫做header-start,终止位置的水平网格线和垂直网格线叫做header-end。

7.6.5 容器属性-grid-auto-flow 内容放置顺序

划分网格后,容器的子元素会按照顺序,自动放置在每一个网格。默认的放置顺序是“先行后列”,也可以通过该属性的设置,先列后行。

  • grid-auto-flow: row; // 默认情况,先行后列排放,即一行一行放,此时根据列宽位置的设置,决定几列,然后将内容换算成对应的行。
  • grid-auto-flow: column; // 先列后行排放,一列一列放,此时根据设置行高的数量决定一共有多少行,然后将内容放置对应行数后生成对应的列
  • grid-auto-flow: column dense;
  • grid-auto-flow: row dense;

7.6.6 容器属性-justify-items/align-items/place-items单元格内容放置位置

align-items 设置单元格内容的垂直位置(上中下)
justify-items 设置单元格内容的水平位置(左中右)
place-items: <align-items> <justify-items> 若不设置第二个值,默认与第一个值相等

  • start: 对齐单元格的起始边缘
  • end:对齐单元格的结束边缘
  • center:单元格内部居中
  • stretch:拉伸,占满整个单元格的整个宽度

7.6.7 容器属性-justify-content/align-content/place-content整个内容区域在容器里面的位置

整个内容区域 -> 指的是表格形成区域
容器 -> 指的是container这个容器,若设置宽高800X800 但表格区域不一定会占满
justify-content 整个内容区域在容器里面的水平位置
align-content 整个内容区域在容器垂直位置
place-content:<align-content><justify-content> 省略第二个值等于第一个值

  • start 对齐容器的起始边框
  • end 对齐容器的结束边框
  • center 容器内部居中
  • stretch 项目大小没有指定是,拉伸占据整个网格容器
  • space-around 每个项目两侧的间隔相等。所以项目之间的间隔比容器边框的间隔大一倍。与flex中的这个属性定义相同
  • space-between 项目与项目之间的间隔相等,项目与容器边框之间没有间隔
  • space-evenly 项目与项目之间的间隔相等,项目与容器边框之间也是同样长度的间隔

7.6.8 容器属性-grid-auto-columns/grid-auto-rows超出指定网格的样式

例如我们定义了3X3网格,那么在第四行或第四列时样式就会变成默认的,此时我们可以通过这两个属性进行定制化

  • grid-auto-rows: 100px;
  • grid-auto-columns: 200px;

7.6.9 容器属性-grid-template/grid合并属性

  • grid-template 是grid-template-columns、grid-template-rows和grid-template-area这三个属性的合并简写形式
  • grid是grid-template-rows、grid-template、grid-template-area、grid-auto-rows、grid-auto-columns、grid-auto-flow这六个属性合并的简写形式

7.6.10 项目属性-grid-column-start/grid-column-end/grid-row-start/grid-row-end 指定项目位置

给某个具体的项目设置这几个值,可以指定为第几条网格线,还可以指定为网格线的名字

  • 指定第几条网格线
.first-project { // class为first-project的项目
	grid-column-start: 2; // 列 第二条竖线  // 两条竖线合并处算一条
	grid-column-end: 4; // 列 第四条竖线
	grid-row-start: 3; // 行 第三条横线 // 两条横线合并出算一条
	grid-row-end: 4; // 行 第四条横线
}
  • 指定网格线的名字
.container {
	display: grid;
	grid-template-columns: [a] 100px [b] 200px [c]300px [d];
	grid-template-rows: [a1] 200px [b1] 100px [c1] 50px [d1];
}

.first-project {
	grid-column-start: a;
	grid-column-end: d;
	grid-row-start: b1;
	grid-row-end: c1;
}

使用这四个属性,如果产生了项目的重叠,则可以使用z-index属性指定项目的重叠顺序。

7.6.11 项目属性-grid-column/grid-row

  • grid-column 是grid-column-start和grid-column-end合并简写
  • grid-row 是 grid-row-start和gird-row-end合并简写形式

7.6.12 项目属性-grid-area 执行项目放在哪一个区域

  • grid-area: e; // 放在某个具体的位置
  • grid-area: <row-start> / <column-start> / <row-end> / <column-end>;

7.6.13 justify-self/align-self/place-self单元格内容位置

justify-self 设置单元格内容的水平位置
align-self 设置单元格内容的垂直位置
place-self: <align-self> <justify-self>; // 若只写一个值,则默认第二个值与第一个相等

  • start 对齐单元格的起始边缘
  • end 对齐单元格的结束边缘
  • center 单元格内部居中
  • stretch 拉伸,占满整个单元格的默认宽度

官方文档
如果有用,点个赞呗~

总结用法,希望可以帮助到你,
我是Ably,你无须超越谁,只要超越昨天的自己就好~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值