整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

3行css实现循环动画,顺便说说浏览器兼容性

前端开发来说,通过动画来提升交互效果是很常见的。在很早以前,做web动画主要通过javascript或者jquery或者flash这样的手段,非常麻烦,自打有了ccs3,做动画就太方便了,只需几行css代码就可以搞定。

这里我们就演示一个常见的循环滚动效果,任务是这样:先准备一个图片,平铺到页面上充满整个屏幕,然后就让画面一直向上循环滚动,形成无边无际的感觉。

虽然可以从网上搜到一些类似的代码,但是鱼龙混杂,无关紧要的代码非常多,不够纯粹。如果要弄明白动画的原理,只有自己动手做一遍才能真正消化吃透。所以我们来一步步原创这个代码,排除所有不必要的基础样式,只说要点,3个步骤你就可以完全掌握其精髓!

第一步:布局

首先,滚动的图片需要放在一个容器里,一行html代码即可完成:

第二步:把图片放进容器

css中body的边界设为0,把容器设高度100%以充满屏幕,再调用背景图pic.png

第三部:让画面动起来

咱不做标题党,循环滚动靠的就是3行css动起来的。

先是1行 -webkit-animation属性:4个参数分别表示:动画名称scroll,1秒时长,移动速度为线性的,无限循环。

然后是对应的关键帧 @-webkit-keyframes 属性,这是自己定义的动画规则,只需写2行规则即可:

原理:动画就是画从一个地方动到另一个地方。对普通滚动效果来说,有起点和终点这两个节点的位置就够了。所以我们用0%和100%分别表示起点和终点,指定2个背景图的xy位置坐标即可。图片会在规定时间内从起点移动到终点,并循环下去,数值是负表示是向上移动。320px正好是图片的高度,这样循环的时候是无缝衔接的。

好了,最终完整的代码如下,是不是很精练呢?保存成 index.html 即可

代码写完了,还要记得在当前目录要有pic.png这个图片哦,我随便画了几笔,绝无观赏性,建议自己找个好看点的图片来代替。

现在用浏览器打开index.html即可看到效果,比较魔性的地方在于,如果你盯着看久了,关闭窗口以后会出现幻觉,仿佛整个显示器都在向上飞,哈哈!

最后我们来说说浏览器兼容性问题:

大家可能注意到了,前面那2个古怪的 -webkit-animation, @-webkit-keyframes 这里的-webkit-其实是一个前缀,animation和@keyframes才是CSS的标准属性。

当加上-webkit-后,就形成了一个针对特殊浏览器的专有属性,表示用在谷歌的chrome和苹果的safari浏览器上。此外还有-moz前缀代表针对firefox浏览器的私有属性。

所以我们在用到css3的一些特性的时候,经常使用一大堆的重复性的代码,比如我们今天的这个代码,有人会写成这个样子:

一个简单的动画就要写这么多冗余的代码,为的只是支持一些旧的浏览器,有必要吗?为什么在这个例子中我们仅仅采用了-webkit-而没有使用其它专有属性呢?

因为现在已经是2019年了!谷歌苹果的浏览器是主流,占据了绝大部分,而其它小众浏览器也大多能够兼容他们,在版本上,大部分人安装浏览器是直接下载新版本安装使用,而非找出家里陈年的老软盘、老光盘去安装,家中的老电脑也早已升级不知多少回了,所以也几乎没有机会使用低版本的浏览器了!

至于微软的IE,就更别提了,IE9以前不支持动画的,只能用js或者jquery来写动画,直到IE10才支持css动画,随后IE被放弃,主推Edge,搞了几天越来越头大干脆也放弃,现在直接使用chrome内核了,所以针对ie的兼容性除非有特殊要求已经无需考虑。

你在网上能看到的范例代码,如果有写成那么复杂臃肿的,估计也都是3-5年前发的老文,或者抄来抄去不做思考的搬砖工留下的“初学者”笔记。

我们不仿测试一下几款主流浏览器的情况看看,结论:

测试结果表明,-webkit-的写法在4款不同内核的浏览器上都能正常使用,所以我们的代码因此能得以简化。

当然,这个例子也有局限性,比如你看,只有苹果safari不支持标准写法,万一将来他改邪归正了呢?毕竟标准写法才是众望所归不是?使用针对个别浏览器的私有属性写法,虽可用但毕竟有些怪怪的,将来怎么样还很难说呢。这样看来,如果使用古老的处理办法,重复N次为每个专属浏览器各写一份代码,除了辣眼睛也真没什么错。

浏览器的兼容问题涉及面实在是非常广,三言两语还真说不完,以后会专门来讲。

详细介绍如何使用 HTML 和 CSS 创建文本与图片的无限滚动动画效果。网页内容包含两个部分,一个是标签组滚动,另一个是图片组滚动,其中动画的效果主要表现为标签和图片一直横向的水平滚动,并且元素会无缝衔接从而实现无限循环,并且首尾两端有渐变蒙层效果,以造成出现和消失的过渡。


HTML 结构

首先,HTML 代码部分包含了5个.scroll元素,这个数量取决于你网页有几个无限滚动区域。每个scroll元素都放了两个div(d1和d2)用于创建滚动容器,其中每个div元素都具有相同的内容元素,用于展示滚动内容。本案例的主要内容就是标签组span和图片组img。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Infinite Scrolling Animation</title>

  <link rel="stylesheet" href="./css/index.css">
</head>

<body>
  <div class="scroll" style="--t: 20s">
    <div>
      <span>HTML</span>
      <span>CSS</span>
      <span>JavaScript</span>
      <span>Vue</span>
      <span>React</span>
      <span>Figma</span>
      <span>Photoshop</span>
    </div>

    <div>
      <span>HTML</span>
      <span>CSS</span>
      <span>JavaScript</span>
      <span>Vue</span>
      <span>React</span>
      <span>Figma</span>
      <span>Photoshop</span>
    </div>
  </div>

  <div class="scroll" style="--t: 30s">
    <!-- 同上 -->
  </div>

  <div class="scroll" style="--t: 10s">
    <!-- 同上 -->
  </div>

  <div class="scroll" style="--t: 35s">
    <!-- 同上 -->
  </div>

  <div class="scroll img-box" style="--t: 25s">
    <div>
      <img src="./images/img_01.jpg" alt="image">
      <img src="./images/img_02.jpg" alt="image">
      <img src="./images/img_03.jpg" alt="image">
      <img src="./images/img_04.jpg" alt="image">
      <img src="./images/img_05.jpg" alt="image">
      <img src="./images/img_06.jpg" alt="image">
      <img src="./images/img_07.jpg" alt="image">
      <img src="./images/img_08.jpg" alt="image">
      <img src="./images/img_09.jpg" alt="image">
    </div>

    <div>
      <!-- 同上 -->
    </div>
  </div>
</body>

</html>

标签组和图片组里的两个div要宽度保持一致,也就是说d1和d2里的每个标签span要对应相同,否则两个div就会出现滚动覆盖或距离过大。 还有就是每个scroll标签的自定义变量--t的值不一样(又快又慢),要想滚动效果统一的话时间调整一样就可以了。

CSS 样式

接下来,看 CSS 部分设置了一些基本的全局样式,有重置样式、内容水平垂直居中布局、背景字体颜色等不做过多赘述。

/* @import url('https://fonts.googleapis.com/css?family=Poppins:400,600,700,800&display=swap'); */
@import './google-fonts.css';

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: 'Poppins', sans-serif;
}

body {
  min-height: 100vh;
  background-color: #222;
  color: #fff;

  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;  
}

/* ... */

滚动容器scroll有一个固定宽度,然后对于溢出的内容隐藏不可见,以及使用 mask-image 创建了一个线性渐变遮罩,给内容带来滚动时的淡出淡入视觉效果。

  • span元素标签的样式设置了内联块级元素的展示方式,以及一些边距、边框、字母间距和文本转换。此外,当鼠标悬停时,还设置了背景颜色的变化。
  • img图片的样式设置了最大宽度和灰度滤镜,当鼠标悬停在图片上时,滤镜会被移除,呈现出原始彩色图片。 两者都提供了一种交互反馈的效果。

.scroll {
  display: flex;
  width: 700px;
  overflow: hidden;
  mask-image: linear-gradient(90deg, transparent, #fff 20%, #fff 80%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #fff 20%, #fff 80%, transparent);
}

.scroll > div span {
  display: inline-block;
  margin: 10px;
  padding: 5px 10px;
  background-color: #333;
  border-radius: 5px;

  letter-spacing: 0.2em;
  text-transform: uppercase;

  cursor: pointer;
  transition: background-color 0.5s;
}
.scroll > div span:hover {
  background-color: #4caf50;
}

.img-box img {
  max-width: 150px;
  filter: grayscale(1);
  
  cursor: pointer;
  transition: filter 0.5s;
}
.img-box img:hover {
  filter: grayscale(0);
}

/* ... */

技术实现

接着就是每个滚动容器内包裹两个一样的div元素,用于创建无缝衔接,下面分别简称为d1和d2。通过 white-space: nowrap 属性确保 div 内的内容不换行,从而使得内容能够水平滚动。

主要通过两个不同的关键帧动画@keyframes关键帧和过渡animation属性控制两个滚动区域实现的滚动效果。让元素以无限循环和线性动画的方式在.scroll滚动容器内移动。拿本案列的img-box元素阐释一下动画执行过程:

  • 首先,它定义的CSS变量是--t: 25s。里面两个div元素都应用了25s匀速无限循环动画。
  • 然后,在d1中应用了延迟25s * -1 = -25s,animate动画从 transform: translateX(100%) 开始,将元素初始位置设置在容器的右侧外部。在动画结束时,元素移动到了容器的左侧外部,即 transform: translateX(-100%)。
  • 最后,在d2中应用了延迟25s / -2 = -12.5s,animate2动画从 transform: translateX(0) 开始,将元素初始位置设置在容器的右侧外部。在动画结束时,元素移动到了容器的左侧更远的位置,即 transform: translateX(-200%)。滚动的距离是比第一个d1滚动区域更远的,这样可以实现错开的滚动效果。

.scroll > div {
  white-space: nowrap;
  animation: animate var(--t) linear infinite;
  animation-delay: calc(var(--t) * -1);
}
@keyframes animate {
  0% {
    transform: translateX(100%);
  }

  100% {
    transform: translateX(-100%);
  }
}

.scroll > div:nth-child(2) {
  animation: animate2 var(--t) linear infinite;
  animation-delay: calc(var(--t) / -2);
}
@keyframes animate2 {
  0% {
    transform: translateX(0);
  }

  100% {
    transform: translateX(-200%);
  }
}



@media screen and (max-width: 768px) {
  .scroll {
    width: 90vw;
  }

  .scroll > div span {
    background-color: #4caf50;
  }

  .img-box img {
    width: 33vw;
    filter: grayscale(0);
  }
}

由于animate延迟小于animate2,所以动画a2先执行,从右向左持续滚动到x: -200%,当滚动到-100%时也就是当前可见区域,此时a1开始执行动画 1 ,从右向左持续滚动到x: -100%,当滚动到100%时也就是当前可见区域,a2已经到-200%了,接着看a1继续滚动到-100%,a2继续从0到-200%,如此循环往复。

最后

通过本篇文章相信能够帮助你更好地使用CSS来创建一个文本与图片无限滚动动画,从而理解掌握和应用这个效果。通过 transform 属性的变化实现了水平滚动效果,使得 div 内的内容能够在容器内水平滚动,呈现出无限循环的连接效果。丰富了网页增添加了动态和交互性。


「绝无仅有」CSS打造吸睛的文本与图片无限滚动动画
原文链接:https://juejin.cn/post/7306442463765544971

现目标:

  1. 可以一次选取多个图片上传;
  2. 上传的图片可以拖动排序;
  3. 点击图片提示删除弹窗,确认后删除该图片;
  4. 点击上传按钮图标,即可浏览图片,隐藏选择文件的文本框;
  5. 选择图片以后自动上传;
  6. 拖拽排序的时候使用transition-group,实现动画效果;

图1 多图上传效果


1、html代码

<tr class=''>
<td width="90" align="right">相关多图</td>
<td >
<div class='yllist yllist_x_duotu'>
<dl>
<!--存放上传的图片-->
<transition-group name="list">
<dd   v-for="(item,index) in listData " draggable="true" :key="item" 
	@click="del(index)" 
	@mouseover="showzz(1,index)"  
	@mouseleave="showzz(0,index)" 
	@dragstart="drag($event,index)" 
	@drop="drop($event,index)"
	@dragover='allowDrop($event)'
	>
    <img :src="item.picpath">
    <div class='zzz none'  :class="{'nonone':item.shs==1}">
    <div class='zzimg '><i class="fa fa-trash-o" aria-hidden="true"></i></div>
	</div>
</dd>
<!--结束-->
</transition-group>
<dd  @click="upbtn"  class='btnclass'><i class="fa fa-camera-retro" aria-hidden="true"></i>
<input type='file' id='multiple' accept="image/*"   multiple="multiple" style='display:none' @change="autoup"  name="ss">
</dd>
</dl>
<div class='clear'></div>
<div>
<span class='itemms'>说明:可以拖动改变顺序</span>
</div>
</div>
</td>
</tr>

说明:

@click="del(index)" 点击删除图片 index为数组的索引 点击的是第几个图片

@mouseover="showzz(1,index)" 鼠标放到上边 出现遮罩层 垃圾桶

@mouseleave="showzz(0,index)" 鼠标离开 遮罩层消失

@dragstart="drag($event,index)" 以下三个 用于拖拽排序

@drop="drop($event,index)"

@dragover='allowDrop($event)'

draggable="true" 设置为true 可以拖动

:key="item" 这里的key 要注意不能等于 index,要不然没有动画效果

img src的属性 是 :src="item.picpath" 不能是src={{item.picpath}}

<div class='zzz none' :class="{'nonone':item.shs==1}"> 设置遮罩层 shs=1的时候显示

上传的选择框设置为display:none隐藏

transition-group用法:

<transition-group name="list"> 实现拖拽的动画效果 后边的name可以随意写 ,但是要和css的.list-move {transition: transform 0.3s;} 【上边自定义的name,我这里是list】-move 设置该css 动画的时间

2、js代码

new Vue({
    el: '#app',
    data(){
		  tagslist:[
		    '网站开发',//存放的标签
			  '网站建设'
		  ],
		  tagsdt:"",  //绑定的标签文本框
		  tagindex:"",//删除标签的序号(索引)
		  listData: [
				  /*
			      {'picpath':'/public/upload/image/20211107/1.jpg',shs:0}
			      
				  shs   显示遮罩层 ,垃圾桶删除标志,0  不显示   1显示
				  */
		  ],
      file:"file",      //用于切换 file  text  实现同一个图片可以连续上传
		  tis:'',           //提示内容
		  showzzc:0,         //弹出框的显示,隐藏 。0 隐藏  1显示
		  showts:0,          //1 弹出提示操作框  2 弹出提示确认框 
		  lisindex:"",      //记录图片的索引
		  datameth:""       //根据这里的参数操作不同的方法
		 }
    },
    methods:{
	  tags:function(){
		  if(this.tagsdt){
		      this.tagslist.push(this.tagsdt); 
		  }
		  this.tagsdt="";
	  },
	  deltag:function(f){
		  this.showzzc=1;
		  this.showts=1;
		  this.tagindex=f;
		  this.datameth='tag';
	  },
	  hidetc:function(){
		  this.showzzc=0;
	  },
 	   del:function(key){
			this.showzzc=1;
			this.showts=1;
			this.lisindex=key;
			this.datameth="delpic";
			//this.listData.splice(key, 1);
		},
	  isdelc:function(){
			if(this.datameth=="delpic"){
		        this.listData.splice(this.lisindex, 1); 
			}
			if(this.datameth=="tag"){
		        this.tagslist.splice(this.tagindex, 1); 
			}
			this.showzzc=0;
	  }, 
  	showzz:function(meth,key){
			//console.log(this.listData[key].shs);
			if(!this.listData[key].shs){
			    this.$set(this.listData[key],'shs',0);
			}
			this.listData[key].shs=meth;
	  }, 
	  upbtn:function(){
		  document.getElementById("multiple").click();
	  },
	  autoup:function(){
		  let that=this;
      that.file="text";  //切换text  file
		  let ups=document.getElementById( "multiple");
		  let formdata = new FormData();
		  if(ups.files[0]){
			  if(ups.files.length>4){
				  this.showzzc=1;
				  this.showts=2;
				  this.tis="一次最多可以选择4张图片上传!";
          that.file="file";
				  return false;
			  }
        for(m=0;m<=ups.files.length-1;m++){
			      formdata.append("file", ups.files[m]);
			      axios.post("/api/uppic", formdata)
					  .then(function (response) {
					    if(response.data.error=='0000'){
							    that.listData.push(response.data.pic);
                  that.file="file";//重新切换为file
							  //console.log(JSON.stringify(that.listData));
						  }else{
							  that.showzzc=1;
							  that.showts=2;
							  that.tis=response.data.msg;
                that.file="file";
							  return false;
						  }
					  })
					  .catch(function (error) {
						  console.log(error);
					  });
          }
			    console.log(ups.outerHTML);
		  }  
    }
})

注意:上传图片以后一定要that.file="file",切换回file,不然会出现只能上传一次,下次选择当前图片不能上传的情况。

  1. 去掉js中的for循环和html的multiple="multiple"选择多个图片,就是单图的上传;
  2. 因为删除弹出提示的地方有2个,实际可能更多,在点击确认的时候不知道操作哪个删除,所以点删除图片或者是点删除信息标签的时候,向datameth赋不同的值,这样我们点击弹窗的确认,读取datameth的值,使用if判断,就可以知道要删除图片还是删除标签;
  3. 上传图片,删除图片用到的弹窗,在前边十七章有说明;

上边的上传是选取多个然后for循环逐个上传的,也可以file使用数组file[]批量提交,如下:

for(m=0;m<=ups.files.length-1;m++){
			      formdata.append("file[]", ups.files[m]);
}
axios.post("/api/uppic", formdata)

但是这样做的话,后台使用

foreach($_FILES as $k=>$v){
}

得到的$v['name']就是数组,需要我们再次for循环,得到单个的图片信息,返回以后的信息因为是数组,push只能一次追加一个,就只能再次循环,感觉很麻烦还不如开始就循环,一个一个的上传。

3、信息标签html

<tr class=''>
<td width="90" align="right">信息标签</td>
<td>
<div class="layui-input-inline tagslist" >
    <span class='tagspan' v-for="(tag,key) in tagslist"   :key="key" @click="deltag(key)">{{tag}}</span>
</div>
<input type="text"  class='inpMain' id='tags' style='width:150px;'  @blur="tags" v-model="tagsdt" /> <span class='itemms'>点击标签可以删除</span>
 <span class='itemms'></span>
</td>
</tr>

输入文本框绑定tagsdt,当我们鼠标离开该文本框的时候,通过blur使用tags方法读取绑定的tagsdt,可以获得输入的内容,这里需要判断是否为空,如果不为空再push进数组:this.tagslist.push(this.tagsdt);

4、php后端代码

foreach($_FILES as $k=>$v){
       $v['name'],$v['size'],$v['tem_name']
  就是图片的基本信息,使用move_uploaded_file移动到指定文件夹
  			$imags['picpath']=$path;
				$imags['shs']=0;
}
exit(json_encode(array('error'=>'0000','pic'=>$imags),JSON_UNESCAPED_UNICODE));

move_uploaded_file用法:

当前文件:$v["tmp_name"],

目标文件:ROOT_PATH.$images_dir.$newname

move_uploaded_file($v["tmp_name"], ROOT_PATH.$images_dir.$newname);

再次强调上传图片,要验证图片的安全性,防止图片木马!