CSS3&JavaScript 图片分隔切换

在这里插入图片描述
这个切换效果在firebox上测试正常,但到了chrome上就没有切换效果了。原因可能是它们在处理transition的流程上有差别,解决方案就是用animation来实现更精细的动画。

(通常只有两个状态,这种简单的情况适用transition比较方便;多个状态就需要适用animation进行详细的控制了)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf8">
<style>
/* 
图片分隔动画有两种思路
(1)大背景是一张静图,每个小方格做动画
(2)连每个小方格都是静态的,方格里面的伪元素做动画
 */
/* 这里提一下 column-count 这个属性,他可以将一段文本分隔成多列,就跟报纸中的分栏一样 */
.louver {
	width: 30em;
	height: 20em;
	border: 1px solid black;
	background-size: 100% 100%;
	position: relative;
	display: flex;
	/** flex-wrap: nowrap|wrap|wrap-reverse|initial|inherit; 默认值是nowrap */
	flex-wrap: wrap;
	overflow:hidden;
}
.louver>span {
	/** border: 1px solid black; */
	position: relative;
	/** width和height对于inline元素是无效的 */
	display:inline-block;
	/** 只有边框占据内部宽高,才能填满整个父元素 */
	box-sizing: border-box;
	/** CSS3的attr 目前还只能用在伪元素里,如果非要这么用,建议使用LESS/SESS
	attr( <attr-name> <type-or-unit>? [ , <fallback> ]? ) 
	width:  calc(100% / attr(data-row px));
	height: calc(100% / attr(data-column px));
	*/
	overflow: hidden;
	border: 1px solid white;
}
.louver>span>span {
	position: absolute;
	width: 1.5em;
	height: 1.5em;
	color: black;
	background-color: white;
	border-radius: 50%;
	/** box-shadow不指定color的话,默认用的是元素的color */
	box-shadow: 0 0 .5em;
	display: flex;
	justify-content: center;
	align-items: center;
	bottom: .3em;
	left: 50%;
	transform: translateX(-50%);
	z-index: 1;
}
.louver>span::before, .louver>span::after {
	/** border: 1px solid red; */
	content: '';
	position: absolute;
	width: 100%;
	height: 100%;
	background-size: inherit;
	background-position: inherit;
}
</style>
<style>
.card-transition::before, .card-transition::after {
	transition: all 1s;
}
/* import表示优先生效,如果没加important的话,如果两个class都指定了相同的属性哪个生效就说不清楚了
(一般是后面的覆盖前面的,详细的覆盖模糊的【比如nth-child就比普通样式优先级高,即使它定义在前面】,行内样式>内嵌>外部) */

.card-flip {
	/** preserve是保持;perspective是透视
	transform-style和perspective都是作用于它们的子元素的
	*/
	transform-style: preserve-3d;
	/** 透视距离必须和3d同时定义才会有3d效果 */
	perspective: 15em;
	/** overflow 属性规定当内容溢出元素框时发生的事情。
	visible 默认值。内容不会被修剪,会呈现在元素框之外。
	hidden 	内容会被修剪,并且其余内容是不可见的。
	scroll 	内容会被修剪,但是浏览器会显示滚动条以便查看其余的内容。
	auto 	如果内容被修剪,则浏览器会显示滚动条以便查看其余的内容。
	inherit 规定应该从父元素继承 overflow 属性的值。
	*/
	overflow: visible !important;
}
.card-flip::before, .card-flip::after {
	/** 隐藏背面 */
	backface-visibility: hidden;
}
.card-flip::after {
	transform: rotateY(180deg);
}
.card-flip-run::before {
	transform: rotateY(180deg) !important;
}
.card-flip-run::after {
	transform: rotateY(360deg) !important;
}
/* 推拉挤出 */
.card-push::before {
	left: 0;
}
.card-push::after {
	left: -100%;
}
.card-push-run::before {
	left: 100% !important;
}
.card-push-run::after {
	left: 0 !important;
}

/* 交叉推拉 */
.card-pushPull::before {
	left: 0;
}
/** odd表示奇数,会被翻译为2n+1;even是偶数,会被翻译为2n */
.card-pushPull:nth-child(odd)::after {
	left: -100%;
}
.card-pushPull:nth-child(even)::after {
	left: 100%;
}
.card-pushPull-run:nth-child(odd)::before {
	left: 100% !important;
}
.card-pushPull-run:nth-child(even)::before {
	left: -100% !important;
}
.card-pushPull-run::after {
	left: 0 !important;
}

/* 按下反弹 */
.card-pressSpring::before {
	top: 0;
}
.card-pressSpring:nth-child(odd)::after {
	top: -100%;
}
.card-pressSpring:nth-child(even)::after {
	top: 100%;
}
.card-pressSpring-run:nth-child(odd)::before {
	top: 100% !important;
}
.card-pressSpring-run:nth-child(even)::before {
	top: -100% !important;
}
.card-pressSpring-run::after {
	top: 0 !important;
}

/** 毛玻璃淡出
.card-blur::before, .card-blur::after {
	transition: transform .5s ease, filter 1s ease, opacity .5s ease;
}
*/
.card-blur::after {
	transform: scale(1.5, 1.5);
	filter: blur(10px);
	opacity: 0;
}
.card-blur-run::before {
	transform: scale(1, 1);
	filter: blur(0);
	opacity: 1;
}
.card-blur-run::before {
	transform: scale(1.5, 1.5) !important;
	filter: blur(10px) !important;
	opacity: 0 !important;
}
.card-blur-run::after {
	transform: scale(1, 1) !important;
	filter: blur(0) !important;
	opacity: 1 !important;
}
</style>
<script>
let imgs = [
"https://www.html5tricks.com/demo/SlidingImagePanels/images/1.jpg",
"https://www.html5tricks.com/demo/SlidingImagePanels/images/2.jpg",
"https://www.html5tricks.com/demo/SlidingImagePanels/images/3.jpg",
"https://www.html5tricks.com/demo/SlidingImagePanels/images/4.jpg"];
let containerClsName = "louver";
let animStyle;
let index = 0;
/*
background-size
	bg-size = [ <length> | <percentage> | auto ]{1,2} | cover | contain
	(1)按指定数值<length>、百分比<percentage>、原图像大小auto 显示
	(2)cover 按比例调整图片,使之正好能自适应整个背景区域的宽高比,长边铺满短边留白
	(3)contain 按比例调整图片,使之刚好能自适应铺满整个背景区域,短边铺满长边溢出的部分被咔嚓
*/
function createCells() {
	//const louver = document.querySelector("." + containerClsName);
	const louver = document.getElementsByClassName(containerClsName)[0];
	//删除其下的所有子元素
	louver.innerHTML = "";

	const row = louver.dataset.row;
	const column = louver.dataset.column;
	const count = row * column;
	const perWidth = louver.offsetWidth / column;
	const perHeight = louver.offsetHeight / row;
	
	//公共的数值写在style里
	const style = document.createElement("style");
	/*
	background-image
	url('URL') 	指向图像的路径。
	none 		默认值。不显示背景图像。
	inherit 	规定应该从父元素继承 background-image 属性的设置。
	*/
	style.textContent = "." + containerClsName + ">span{width:" +  100/column + "%;height:" + 100/row + "%;background-size:" + column*100 + "% " + row*100 + "%;}";
	for(let i = 1; i <= count; i++) {
		//nth-child 的第一个子元素的下标是1
		//background-position: left top
		style.appendChild(document.createTextNode("." + containerClsName + ">span:nth-child(" + i + "){background-position:" + ((i-1)%column)*-100 + "% " + parseInt((i-1)/column)*-100 + "%}"));
		//本来是在after里面做序号,但如果这样的话动画就要另一个元素了
		//style.appendChild(document.createTextNode("." + containerClsName + ">span:nth-child(" + i + ")::before{content:'" + i + "'}"));
		const child = document.createElement("span");
		child.innerHTML = "<span>" + i + "</span>";
		louver.append(child);
	}
	document.head.appendChild(style);
	const backgroundUrlStyle = document.createElement("style");
	backgroundUrlStyle.id = "backgroundUrlStyle";
	document.head.appendChild(backgroundUrlStyle);
}

function setAnimStyle(_animStyle) {
	animStyle = _animStyle;
	const louvers = document.getElementsByClassName(containerClsName);
	for(let i=0; i<louvers.length; i++) {
		const childs = louvers[i].children;
		for(let j=0; j<childs.length; j++) {
			//由于classList没有清空所有的方法,所以只能用className		
			childs[j].className = animStyle;
			//要临时禁用动画,必须先改变值,然后调用getComputedStyle,触发recalculateStyle,之后再设置transition
			getComputedStyle(childs[j]).length;
			childs[j].classList.add("card-transition");
		}
	}
}
//该方法要求不执行过渡动画
function setImage(index) {
	const next = (index+1) % imgs.length;
	const backgroundUrlStyle = document.getElementById("backgroundUrlStyle");
	backgroundUrlStyle.innerHTML="." + containerClsName + ">span::after{background-image:url(" + imgs[next] + ");}";
	backgroundUrlStyle.appendChild(document.createTextNode("." + containerClsName + ">span::before{background-image:url(" + imgs[index] + ");}"));
	/*
	const louvers = document.getElementsByClassName(containerClsName);
	for(let i=0;i<louvers.length; i++) {
		louvers[i].style.backgroundImage = "url(" + imgs[index] + ")";
	}
	*/
}
//该方法要求每次都执行过渡动画
function changeImage() {
	setImage(index);
	setAnimStyle(animStyle);
	index = ++index % imgs.length;
	const e = window.event || arguments.callee.caller.arguments[0]; //火狐没定义event,因此必须用这种方式做兼容
	//currentTarget是绑定事件的元素;target是实际被点击的元素;事件从源冒泡到上层,除非调用了stopPropagation停止传递
	//e.currentTarget.classList.toggle("flip");
	//直接改父元素的class,然后在css中控制对应的子元素样式也是可以的,但是这里为了复用之前定义的css变量不这么操作
	//JS forEach()方法不能用于HTMLCollection NodeList等类似于数组但又不是真正的数组。
	const childs = e.currentTarget.children;
	for(let i=0; i<childs.length; i++) {
		childs[i].classList.toggle(animStyle + "-run");
	};
}
function setListener() {
	document.querySelectorAll("input[name='animStyle']").forEach((elem)=>{
		elem.onchange=()=>{
			index = 0;
			setAnimStyle("card-" + elem.value);
			setImage(0);
		}
	});
	document.getElementsByName("numberVisible")[0].onchange = function() {
		const louvers = document.getElementsByClassName(containerClsName);
		for(let i=0; i<louvers.length; i++) {
			const childs = louvers[i].children;
			for(let j=0; j<childs.length; j++) {
				const firstChild = childs[j].firstChild;
				if(firstChild.localName == "span") {
					firstChild.style.display = this.checked ? "flex" : "none";
				}
			}
		}
	};
}
window.onload = ()=>{
	createCells();
	setImage(0);
	setListener();
	setAnimStyle("card-flip");
};
</script>
</head>

<body>
<div class="louver" data-row="2" data-column="4" onclick="changeImage()"></div>
<form action="" method="get" style="display:inline-block;">
	<input type="checkbox" name="numberVisible" checked="checked"><label for="flip">NumberVisible</label></input>
	<input type="radio" name="animStyle" value="flip" id="flip" checked="checked"><label for="flip">flip</label></input>
	<input type="radio" name="animStyle" value="push" id="push"><label for="push">push</label></input>
	<input type="radio" name="animStyle" value="pushPull" id="pushPull"><label for="pushPull">pushPull</label></input>
	<input type="radio" name="animStyle" value="pressSpring" id="pressSpring"><label for="pressSpring">pressSpring</label></input>
	<input type="radio" name="animStyle" value="blur" id="blur"><label for="blur">blur</label></input>
</form>
</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值