SS即层叠样式表(Cascading StyleSheet)。 在网页制作时采用层叠样式表技术,可以有效地对页面的布局、字体、颜色、背景和其它效果实现更加精确的控制。
今天给大家写了两个特效,好玩又好看,实不实用就看你怎么用
css3的这两个特效源码项目文末有领取地址
一、css3点击按钮气泡动画特效
css3点击按钮
css3点击按钮气泡动画特效源码展示
二、CSS3绘制游动的鱼
CSS3绘制游动的鱼部分源码
学习前端特效欢迎加入web前端网站开发学习群640633433
果图
各位观众大家好,今天给大家带来的是
jQuery制作海底世界鱼群游动动画特效源码
附带滑屏切换特效!
是不是很炫酷!
废话不多说上源码
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>jQuery海底世界鱼群游动动画特效</title>
<script type="text/javascript" src="js/jquery.min.js"></script>
<style>
html,body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
.container{
position: relative;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
canvas{
position: absolute;
top: 0;
left: 0;
}
</style>
</head>
<body><script src="/demos/googlegg.js"></script>
<div id="jsi-sea-container" class="container"></div>
<script type="text/javascript">
var HAMMERHEAD_RENDERER={
HAMMERHEAD_COUNT : 10,
ADD_INTERVAL : 3,
DELTA_THETA : Math.PI / 1000,
ADJUST_DISTANCE : 50,
ADJUST_OFFSET : 10,
init : function(){
this.setParameters();
this.reconstructMethod();
this.createHammerHeads(this.INIT_HAMMERHEAD_COUNT);
this.bindEvent();
this.render();
},
setParameters : function(){
this.$window=$(window);
this.$container=$('#jsi-sea-container');
this.width=this.$container.width();
this.height=this.$container.height();
this.context=$('<canvas />').attr({width : this.width, height : this.height}).appendTo(this.$container).get(0).getContext('2d');
this.interval=this.ADD_INTERVAL;
this.distance=Math.sqrt(Math.pow(this.width / 2, 2) + Math.pow(this.height / 2, 2));
this.x=this.width;
this.destinationX=this.x;
this.theta=0;
this.hammerheads=[];
},
reconstructMethod : function(){
this.render=this.render.bind(this);
},
createHammerHeads : function(){
for(var i=0, length=this.HAMMERHEAD_COUNT; i < length; i++){
this.hammerheads.push(new HAMMERHEAD(this.width, this.height));
}
},
bindEvent : function(){
this.$container.on('mousemove', this.changeView.bind(this, false));
this.$container.on('mouseout', this.changeView.bind(this, true));
},
changeView : function(toAdjust, event){
this.destinationX=event.clientX - this.$container.offset().left + this.$window.scrollLeft();
if(!toAdjust){
return;
}
if(this.destinationX < this.ADJUST_OFFSET){
this.destinationX=0;
}else if(this.distanceX > this.width - this.ADJUST_OFFSET){
this.destinationX=this.width;
}
},
render : function(){
requestAnimationFrame(this.render);
var gradient=this.context.createRadialGradient(this.width / 2, this.height / 2, 0, this.width / 2, this.height / 2, this.distance),
rate=(1 + 0.2 * Math.sin(this.theta));
gradient.addColorStop(0, 'hsl(195, 80%, ' + (90 * rate) + '%)');
gradient.addColorStop(0.2, 'hsl(195, 100%, ' + (50 * rate) + '%)');
gradient.addColorStop(1, 'hsl(220, 100%, ' + (10 * rate) + '%)');
this.context.fillStyle=gradient;
this.context.fillRect(0, 0, this.width, this.height);
this.hammerheads.sort(function(hammerhead1, hammerhead2){
return hammerhead1.z - hammerhead2.z;
});
for(var i=this.hammerheads.length - 1; i >=0; i--){
if(!this.hammerheads[i].render(this.context)){
this.hammerheads.splice(i, 1);
}
}
this.context.clearRect(this.x, 0, this.width - this.x, this.height);
if(this.interval--==0){
this.interval=this.ADD_INTERVAL;
this.hammerheads.push(new HAMMERHEAD(this.width, this.height));
}
this.theta +=this.DELTA_THETA;
this.theta %=Math.PI * 2;
if(this.destinationX > this.x){
this.x=Math.min(this.x + this.ADJUST_DISTANCE, this.destinationX);
}else{
this.x=Math.max(this.x - this.ADJUST_DISTANCE, this.destinationX);
}
}
};
var HAMMERHEAD=function(width, height){
this.width=width;
this.height=height;
this.init();
};
HAMMERHEAD.prototype={
COLOR : 'hsl(220, %s%, 30%)',
ANGLE_RANGE : {min : -Math.PI / 8, max : Math.PI / 8},
INIT_SCALE : 0.1,
MAX_Z : 10,
DELTA_PHI : Math.PI / 80,
VELOCITY : 3,
VERTICAL_THRESHOLD : 80,
init : function(){
this.theta=this.ANGLE_RANGE.min + (this.ANGLE_RANGE.max - this.ANGLE_RANGE.min) * Math.random();
this.x=this.width / 2 + this.width / 4 * this.theta / Math.PI * 8;
this.y=this.height + this.VERTICAL_THRESHOLD * this.INIT_SCALE;
this.z=Math.random() * this.MAX_Z;
this.vx=-this.VELOCITY * Math.cos(this.theta + Math.PI / 2);
this.vy=-this.VELOCITY * Math.sin(this.theta + Math.PI / 2);
this.phi=Math.PI * 2 * Math.random();
this.color=this.COLOR.replace('%s', 90 - 60 * this.z / this.MAX_Z | 0);
},
render : function(context){
var tailX=20 * Math.sin(this.phi),
angle=Math.sin(this.phi),
height=this.height + this.VERTICAL_THRESHOLD,
scale=this.INIT_SCALE + (1 - this.INIT_SCALE) * (height - this.y) / height * (this.MAX_Z - this.z) / this.MAX_Z;
context.save();
context.fillStyle=this.color;
context.translate(this.x, this.y);
context.scale(scale, scale);
context.rotate(this.theta);
context.beginPath();
context.moveTo(-20, -40);
context.bezierCurveTo(-8, -48, 8, -48, 20, -40);
context.lineTo(20, -28);
context.lineTo(8, -36);
context.lineTo(8, -8);
context.lineTo(20, 4 + 6 * angle);
context.lineTo(8, 0);
context.lineTo(6, 16);
context.quadraticCurveTo(4, 32, tailX, 64);
context.quadraticCurveTo(-4, 32, -6, 16);
context.lineTo(-8, 0);
context.lineTo(-20, 4 - 6 * angle);
context.lineTo(-8, -8);
context.lineTo(-8, -36);
context.lineTo(-20, -28);
context.closePath();
context.fill();
context.save();
context.beginPath();
context.translate(tailX, 64);
context.rotate(-Math.sin(this.phi) * Math.PI / 6);
context.moveTo(0, -5);
context.lineTo(10, 15);
context.lineTo(0, 5);
context.lineTo(-10, 15);
context.closePath();
context.fill();
context.restore();
context.restore();
this.x +=this.vx * scale;
this.y +=this.vy * scale;
this.phi +=this.DELTA_PHI;
this.phi %=Math.PI * 2;
return this.y >=-this.VERTICAL_THRESHOLD;
}
};
var MANTA_RENDERER={
MANTA_COUNT : 3,
ADD_INTERVAL : 30,
DELTA_THETA : Math.PI / 1000,
init : function(){
this.setParameters();
this.reconstructMethod();
this.createMantas();
this.render();
},
setParameters : function(){
this.$container=$('#jsi-sea-container');
this.width=this.$container.width();
this.height=this.$container.height();
this.context=$('<canvas />').attr({width : this.width, height : this.height}).appendTo(this.$container).get(0).getContext('2d');
this.interval=this.ADD_INTERVAL;
this.distance=Math.sqrt(Math.pow(this.width / 2, 2) + Math.pow(this.height / 2, 2));
this.theta=0;
this.mantas=[];
},
reconstructMethod : function(){
this.render=this.render.bind(this);
},
createMantas : function(){
for(var i=0, length=this.MANTA_COUNT; i < length; i++){
this.mantas.push(new MANTA(this.width, this.height, this.context));
}
},
render : function(){
requestAnimationFrame(this.render);
var gradient=this.context.createRadialGradient(this.width / 2, this.height / 2, 0, this.width / 2, this.height / 2, this.distance),
rate=(1 + 0.2 * Math.sin(this.theta));
gradient.addColorStop(0, 'hsl(195, 80%, ' + (60 * rate) + '%)');
gradient.addColorStop(0.2, 'hsl(195, 100%, ' + (40 * rate) + '%)');
gradient.addColorStop(1, 'hsl(220, 100%, ' + (5 * rate) + '%)');
this.context.fillStyle=gradient;
this.context.fillRect(0, 0, this.width, this.height);
this.mantas.sort(function(manta1, manta2){
return manta1.z - manta2.z;
});
for(var i=this.mantas.length - 1; i >=0; i--){
if(!this.mantas[i].render(this.context)){
this.mantas.splice(i, 1);
}
}
if(this.interval--==0){
this.interval=this.ADD_INTERVAL;
this.mantas.push(new MANTA(this.width, this.height, this.context));
}
this.theta +=this.DELTA_THETA;
this.theta %=Math.PI * 2;
}
};
var MANTA=function(width, height, context){
this.width=width;
this.height=height;
this.init(context);
};
MANTA.prototype={
COLOR : 'hsl(200, %s%, %l%)',
ANGLE_RANGE : {min : -Math.PI / 8, max : Math.PI / 8},
INIT_SCALE : 0.3,
RANGE_Z : {min : 0, max : 30},
DELTA_ANGLE : Math.PI / 160,
VELOCITY : 2,
VERTICAL_THRESHOLD : 400,
init : function(context){
this.angle=this.getRandomValue(this.ANGLE_RANGE);
this.x=this.width / 2 + this.width / 3 * this.angle / Math.PI * 8;
this.y=this.height + this.VERTICAL_THRESHOLD * this.INIT_SCALE;
this.z=this.getRandomValue(this.RANGE_Z);
this.vx=-this.VELOCITY * Math.cos(this.angle + Math.PI / 2);
this.vy=-this.VELOCITY * Math.sin(this.angle + Math.PI / 2);
this.phi=Math.PI * 2 * Math.random();
this.theta=Math.PI * 2 * Math.random();
this.psi=Math.PI * 2 * Math.random();
var color=this.COLOR.replace('%s', 60),
luminance=20 * this.z / this.RANGE_Z.max | 0;
this.gradient=context.createLinearGradient(-140, 0, 140, 0);
this.gradient.addColorStop(0, color.replace('%l', 10 + luminance));
this.gradient.addColorStop(0.1, color.replace('%l', 10 + luminance));
this.gradient.addColorStop(0.5, color.replace('%l', 20 + luminance));
this.gradient.addColorStop(0.9, color.replace('%l', 10 + luminance));
this.gradient.addColorStop(1, color.replace('%l', 10 + luminance));
this.color=this.COLOR.replace('%s', 100).replace('%l', 5 + luminance);
},
getRandomValue : function(range){
return range.min + (range.max - range.min) * Math.random();
},
render : function(context){
var height=this.height + this.VERTICAL_THRESHOLD,
scale=this.INIT_SCALE + (1 - this.INIT_SCALE) * (height - this.y) / height * (this.RANGE_Z.max - this.z) / this.RANGE_Z.max * 2,
top=(Math.sin(this.phi) < 0 ? 50 : 60) * Math.sin(this.phi);
context.save();
context.translate(this.x, this.y);
context.scale(scale, scale);
context.rotate(this.angle);
context.fillStyle=this.color;
context.beginPath();
context.moveTo((225 + top) / 4, -20);
context.lineTo((210 + top) / 4, 70 / 4);
context.lineTo(-(210 + top) / 4, 70 / 4);
context.lineTo(-(225 + top) / 4, -20);
context.closePath();
context.fill();
context.lineWidth=5;
context.strokeStyle=this.gradient;
context.beginPath();
context.moveTo(0, 70);
context.quadraticCurveTo(0, 130, 20 * Math.sin(this.theta), 190);
context.stroke();
context.fillStyle=this.gradient;
context.beginPath();
context.moveTo(-15, -40);
context.bezierCurveTo(-10, -35, 10, -35, 15, -40);
context.lineTo(30, -40);
context.quadraticCurveTo(35, -40, 45, -30);
context.quadraticCurveTo(50, -25, 80 + top, 0);
context.quadraticCurveTo(60, 0, 10, 70);
context.lineTo(-10, 70);
context.quadraticCurveTo(-60, 0, -80 - top, 0);
context.quadraticCurveTo(-50, -25, -45, -30);
context.quadraticCurveTo(-35, -40, -30, -40);
context.lineTo(-15, -40);
context.closePath();
context.fill();
context.lineWidth=12;
context.strokeStyle=this.gradient;
context.beginPath();
context.moveTo(23, -38);
context.quadraticCurveTo(33, -55, 23 - 10 * Math.sin(this.psi), -70);
context.stroke();
context.beginPath();
context.moveTo(-23, -38);
context.quadraticCurveTo(-33, -55, -23 + 10 * Math.sin(this.psi), -70);
context.stroke();
context.lineWidth=1;
context.strokeStyle=this.color;
context.beginPath();
for(var i=0; i < 5; i++){
var y=-10 + i * 8 + (1 - Math.sin(this.phi)) * 3;
context.moveTo(10, -20 + i * 8);
context.quadraticCurveTo(20, -15 + i * 8, 30, y);
context.moveTo(-10, -20 + i * 8);
context.quadraticCurveTo(-20, -15 + i * 8, -30, y);
}
context.stroke();
context.restore();
this.x +=this.vx * scale;
this.y +=this.vy * scale;
this.phi +=this.DELTA_ANGLE;
this.phi %=Math.PI * 2;
this.theta +=this.DELTA_ANGLE;
this.theta %=Math.PI * 2;
this.psi +=this.DELTA_ANGLE;
this.psi %=Math.PI * 2;
return this.y >=-this.VERTICAL_THRESHOLD;
}
};
$(function(){
MANTA_RENDERER.init();
HAMMERHEAD_RENDERER.init();
});
</script>
<div style="text-align:center;margin:50px 0; font:normal 14px/24px 'MicroSoft YaHei';">
</div>
</body>
</html>
品:科普中国
制作:苏澄宇
监制:中国科学院计算机网络信息中心
这看似是一个理所应当的问题:"因为鱼有鳍,就像人有脚一样,所以鱼会游泳"。
如果嫌这句话麻烦甚至可以用"因为它是鱼,所以它会游泳"这种方式来回答。
但真相真的是这样吗?鱼鳍是鱼游泳的主要动力来源吗?
如果深究下来,你会发现这个问题其实并不简单。
图源:Giphy 动图
很早之前,人们就认识到,游泳是动物最省力的运动方式。
走路、飞行、游泳,如果让你选择一种方式从A点移动到B点,你会怎么选?
我想大部分人都会选择飞行。
确实,对于个体来说,飞行是最快的运动方式,但如果单从消耗能量大小的角度来考虑,飞行却不是最优解。
科学家研究发现,同样要移动1km,松鼠爬行需要消耗22.73J的热量;海鸥飞行需要消耗6.07J的热量;鲑鱼游泳只需要消耗1.63J的热量,大约只有松鼠的1/20,海鸥的1/6。
就算是找一个和鲑鱼一模一样的物体,用同样的速度把它放到水里拖动它移动1km,科学家发现会游动的鲑鱼消耗的能量只有前者的1/7-1/8。
很显然,游泳是动物最省力的方式。
鱼既然游泳那么高效,人类肯定会想去模仿它,去创造一个和鱼运动方式一样的水下推进装置。
你也许会想,我们不是已经有潜艇了吗?
但即使是最快的核潜艇,用螺旋桨推进的它时速只有44.7节,也就是每小时80.4公里。水里游得最快的旗鱼,速度可以达到每小时112公里。
不止是速度的问题,潜艇推进时噪音大、耗能高,并且高速旋转的螺旋桨还会产生空蚀效应,导致螺旋桨磨损。
所以人类一直想去模仿鱼的游动方式,设计出更先进的水下载具,这也就是仿生。
但想去模仿,就得先搞懂鱼游泳的原理。
那第一个问题来了,选哪种鱼作为研究对象呢?
如果仔细观察鱼的运动方式,你会发现大部分的鱼运动方式大抵相同:利用尾部和身躯肌肉的收缩进行有规律的左右扭动,专业上这称为身体/尾鳍推进模式(Body and/or caudal fin, BCF)。
鱼的游动方式|图源:https://tpwd.texas.gov/kids/wild_things/fish/howdofishswim.phtml
科学家发现,不同的鱼,扭动的幅度大小和频率不太一样,但核心都是在于扭动。
当然,鱼鳍的摆动在游动时也起到一定的作用,主要是调整方向,最主要的动力还是来自于身躯的扭动,特别是当鱼快速移动的时候。
所以,鱼鳍并不是鱼游泳的主要的动力来源,而是躯干的扭动。
不同鱼的鱼鳍大小、形状不一样|图源:https://tpwd.texas.gov/kids/wild_things/fish/howdofishswim.phtm
既然鱼的主要运动方式是靠躯干的扭动,那就研究它们扭动的细节就好了。
前面说了,不同的鱼摆动的幅度不太一样,有的幅度大,有的幅度小。扭动幅度大的称为波动式,扭动幅度小的称为摆动式。不
管怎么分类,这两种推进模式在基本运动原理都一样。
不同的鱼扭动的幅度不一样|图源:文献3
而作为科学研究,肯定优先找那些摆动幅度大的鱼来作为研究对象,因为运动幅度越大,越容易观察到摆动的细节。
那什么鱼摆动幅度比较大呢?
自然是黄鳝、鳗鲡之类的长条鱼,因为身型越长条,利用肌肉收缩为动力产生的波浪式运动越明显。
鳗鱼的运动方式主要依靠躯体的波浪式运动|图源:giphy 动图
所以很早的时候,科学家就拿鳗鱼之类的长条鱼来做实验,主要用高速摄影的方式来拍摄它们的运动方式。
利用摄影技术来研究鱼的运动方式,有很大的局限性。
鱼是自由的,它在水箱里游来游去都是随心所欲的,更不会做出特定的动作来满足科学家的研究需求。
为了定量分析鱼在游动过程中的振幅、频率、前进速度等参数,有科学家设计了一个能让一条死鱼躯体产生波状游泳动作的机械装置。
这个装置通过在鱼的身体上插一排长杆,然后摇动装置上的凸轮来控制鱼的动作。
当凸轮转动的时候,动杆就会驱动鱼体产生预定的波状运动。
这样科学家就可以让鱼按照自己想要的方式进行运动,可以更方便地研究出具体的参数。
记录下鱼运动的方式很多,但不管什么研究方式,最后都还是要总结成理论模型,才能了解运动的具体机制。
通过观察科学家发现,在鱼的游动过程中,鱼体的肌肉会按从头至尾的顺序进行收缩,身体逐个部位弯曲,产生向后的运动波,进而推动水流产生向前的推力。
鲑鱼的肌肉排列顺序|图源:https://tpwd.texas.gov/
至于这个推力是如何产生的,主要有两个理论来解释:
阻力理论(RFT)& 细长体理论(EBT)
这是英国物理学家弗里·泰勒(Geoffrey Taylor)在1952年提出的,在阻力理论中,一个物体会被分割成无穷小的部分,每一个部分都会产生推力和阻力。
当鱼产生波浪式运动时,那么垂直于鱼体方向的阻力比平行于鱼体方向的阻力大。其结果是在平行方向,也就是前进的方向上,产生一个推力。
阻力理论|图源:AIP Publish
这是一位英国数学家詹姆斯·莱特希尔(James Lighthill)在1960年提出的,和前面的阻力理论完全不一样,他认为推动鱼前进主要依靠的是水的惯性。
这使得鱼体作为一个平面,可以通过小振幅的波动产生推力。
这两个理论之间的最主要区别在于所产生的力量的类型。
泰勒理论认为,让鱼向前游动的力产生于阻力,阻力的作用方向和鱼的运动方向相反,但与物体的运动速度相一致;莱特希尔则认为鱼向前的游动的力产生于反作用力,其作用方向与作用力相反,并与加速度保持一致。
后来一个北京计算机科学研究中心的团队,通过超级计算机的模拟,对两个理论进行了验证。
之后发现两个理论都是对的,但不同的鱼情况不太一样。
这主要还是和鱼的形状有关,长条形的鱼,比如鳗鱼,它在波动的时候,躯干部分产生的阻力是最主要的,因为这部分力的作用相对平滑和均匀。
模拟鳗鱼的主要运动方式|图源:文献1
而对于鲭鱼,这类形状比较普通的鱼,它在摆动的时候,虽然也依靠躯干部分的阻力,但尾鳍左右摆动产生的反作用力也同样很重要。
模拟鲭鱼的运动方式|图源:文献1
简单来说,身型越长条的鱼,越依靠躯干部分产生的作用力,身型越短的鱼,越依靠尾鳍产生的作用力。
将动力移到尾部的鱼有一个缺点,那就是鱼体很容易失去侧向平衡,也就是说,它一摆尾就可能会引起头部的摇摆。
对于这点,鲅鱼通过演化,把自己身体中部垂直面提高,增重躯干前面的重量(可以说是增加配重),来增大左右摆动的阻力,避免了游泳的时候偏来偏去。
虽然科学家最终搞清楚了鱼到底是如何游动的,但他们并没有搞懂鱼游动过程中能量是如何转移利用的,这也是为什么直到现在,我们还没能造出一个完美的仿生鱼游动装置。
之前造出来的仿生鱼虽然可以模拟鱼的游动效果,但问题还有一大堆:要么游得太慢,要么功耗太高,要么躯体过大,要么结构复杂。
各式各样的仿生鱼|图源:文献2
鱼游泳很容易见到,但即使是身边常见的现象,要搞清楚原理也很难,要复制它则更难。
在大自然面前,人类的知识真的好渺小…
参考文献:
[1]https://www.technologyreview.com/2018/12/18/138543/we-finally-know-how-fish-swim-so-fast/
[2]https://mall.cnki.net/magazine/article/HLGX199202002.htm
[3]http//tow.cnki.net/kcms/detail/detail.aspx?filename=JXXB201617015&dbcode=CRJT_CJFD&dbname=CRJT_CJFDTOTAL&v=
[4]https://epubs.siam.org/doi/abs/10.1137/1.9781611970517.ch5
*请认真填写需求信息,我们会在24小时内与您取得联系。