敞开成长之旅!这是我参与「日新方案 12 月更文应战」的第5天,点击检查活动详情
前沿
上一篇《粒子相册(上)》咱们完成了由粒子构成图片的作用,今天咱们继续来完善后续功用,完成出一个完好的粒子相册。
作用演示
先演示下完成后的作用,因为视频转 GIF 后会呈现明显的卡帧,主张咱们把项目 clone 下来自行运行体会。源码
完成
一、完成相册功用
粒子相册自然是离不开相册功用,这儿咱们运用 carousel_slider 插件来完成图片的轮播展现。
新增 CarouselSlider
组件,设置 autoPlay
为 true 用于自动播放,轮播间隔设为 6s
CarouselSlider(
options: CarouselOptions(
aspectRatio: 2.2,
enlargeCenterPage: true,
initialPage: 2,
autoPlay: true,
autoPlayInterval: const Duration(seconds: 6),
onPageChanged: _onPageChanged,
),
items: imageSliders,
)
在 imageSliders
中界说图片的展现样式,这儿用 Image
和 Text
展现图片和称号
final List<Widget> imageSliders = imgList
.map((item) => Container(
margin: const EdgeInsets.all(5.0),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(5.0)),
child: Stack(
children: <Widget>[
Image.asset(item, fit: BoxFit.cover, width: 1000.0),
Positioned(
bottom: 0.0,
left: 0.0,
right: 0.0,
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
Color.fromARGB(200, 0, 0, 0),
Color.fromARGB(0, 0, 0, 0)
],
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
),
),
padding: const EdgeInsets.symmetric(
vertical: 10.0, horizontal: 20.0),
child: Text(
'No. ${imgList.indexOf(item)} image',
style: const TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
),
],
)),
)).toList();
最终在 _onPageChanged
中监听图片的切换,在每次切换轮播图片后从头加载新的粒子图片
void _onPageChanged(int index, CarouselPageChangedReason reason) {
debugPrint("index=$index, ${reason.toString()}");
particleManage.reset();
initImage(index: index);
}
优化图片的尺寸不一致问题,因为每张图片的尺寸都是不固定的,为了让展现的作用一致,这儿需求对展现的图片进行裁剪缩放处理
/// 图片转化粒子
void imageToParticle(){
if(imagePic == null) return;
int width = imagePic!.width;
int height = imagePic!.height;
double aspect = width / height;
int size = min(width, height);
int left = aspect > 1 ? (width - height) ~/ 2 : 0;
int top = aspect < 1 ? (height - width) ~/ 2 : 0;
for(int i = 0; i < 200; i++) {
for(int j = 0; j < 200; j++) {
// image库获取的x、y和Flutter相反,需求把j做为x轴
int x = left + j * size ~/ 200;
int y = top + i * size ~/ 200;
...
}
}
}
完成作用:
假如仅仅只是完成如此功用,自然是没有必要从头写一篇文章,下面让咱们进入今天的正式环节。
二、粒子特点配置
咱们在外面的粒子图片中新增一个进入粒子详情的入口,用于粒子详情页的展现
Navigator.pushNamed(context, "/detail", arguments: imgList[pageIndex]);
前面粒子图片的展现都只是简略的展现了下作用,还不行完善,咱们在详情页中增加愈加详尽的粒子特点设置,便于用户自界说配置。
- 粒子颗粒度
咱们先在粒子管理器 PrticleManage
中新增 granularity
用于界说粒子的颗粒度
// 粒子颗粒度
int granularity = 200;
然后运用 Slider
组件来完成颗粒度特点的调节
Slider(
value: granularity,
min: 50,
max: 400,
activeColor: Colors.redAccent,
divisions: 7,
inactiveColor: Colors.green.withAlpha(99),
onChanged: (value) => _onSliderChange(value, SliderType.granularity),
onChangeEnd: (value) =>
_onSliderChangeEnd(value, SliderType.granularity),
)
最终在 _onSliderChangeEnd
回调中更新设置的粒子颗粒度
particleManage.setGranularity(value);
imageToParticle();
/// 初始化粒子
void initParticles() {
List<Particle> list = [];
double size = 400 / granularity;
for (int i = 0; i < granularity; i++) {
for (int j = 0; j < granularity; j++) {
...
}
}
setParticleList(list);
}
完成作用:
- 粒子运动速度
同理,咱们新增在 ParticleManage
中新增 speed
特点,用来界说例子的移动速度
// 粒子移动速度
double speed = 5.0;
界说 Slider
的特点值设置,最大速度设为 1 最小设为 10
Slider(
value: speed,
min: 1.0,
max: 10.0,
activeColor: Colors.redAccent,
divisions: 9,
inactiveColor: Colors.green.withAlpha(99),
onChanged: (value) => _onSliderChange(value, SliderType.speed),
onChangeEnd: (value) => _onSliderChangeEnd(value, SliderType.speed),
)
在 _onSliderChangeEnd
中更新粒子速度
particleManage.setSpeed(value);
if (this.speed == speed) return;
this.speed = speed;
for (Particle particle in particleList) {
particle.ax = speed + random.nextDouble() * 10;
particle.ay = speed + random.nextDouble() * 10;
}
- 粒子离散规模
粒子离散规模的设置也是一样,界说 Slider
最小为 50 最大 300 。相同经过 _onSliderChangeEnd
在 ParticleManage
中更新粒子参数
Slider(
value: range,
min: 50.0,
max: 300.0,
activeColor: Colors.redAccent,
divisions: 5,
inactiveColor: Colors.green.withAlpha(99),
onChanged: (value) => _onSliderChange(value, SliderType.range),
onChangeEnd: (value) => _onSliderChangeEnd(value, SliderType.range),
)
particleManage.setRange(value);
if (this.range == range) return;
this.range = range;
for (Particle particle in particleList) {
particle.cx = particle.x - (random.nextDouble() * range - range ~/ 2);
particle.cy = particle.y - (random.nextDouble() * range - range ~/ 2);
}
完成作用:
三、粒子动画
上一篇文章咱们介绍了打印机动画、和粒子动画,咱们先把它们整合进来。在 actions
中增加 PopupMenuButton
用于多种动画的切换
PopupMenuButton(
itemBuilder: (context) {
return [
const PopupMenuItem<int>(
value: 0,
child: Text("打印动画"),
),
const PopupMenuItem<int>(
value: 1,
child: Text("粒子运动动画"),
),
];
},
onSelected: _onAnimChanged,
)
在 ParticleManage
中新增 anim
特点,并新增 Anim
枚举用于粒子动画的类型界说
// 粒子动画类型
Anim anim = Anim.particleMotion;
enum Anim {
printer,
particleMotion,
}
最终经过在 ParticleManage
的 reset
办法中从头界说粒子的当时方位和加速度来完成不同的动画作用
case Anim.printer: // 打印机动画
particle.cx = particle.x;
particle.cy = particle.y - 400;
particle.ax = speed;
particle.ay = speed + 2;
break;
case Anim.particleMotion: // 粒子运动
particle.cx = particle.x - (random.nextDouble() * range - range ~/ 2);
particle.cy = particle.y - (random.nextDouble() * range - range ~/ 2);
particle.ax = speed + random.nextDouble() * 10;
particle.ay = speed + random.nextDouble() * 10;
break;
- 原点动画
咱们新增第 3 种动画原点动画,动画是以中心点为原点开端从小到大展现整张图片
粒子的 cx
、cy
和 ax
、ay
的特点设置如下:
int index = granularity ~/ 2;
particle.cx = index * particle.size - particle.size * 0.5;
particle.cy = index * particle.size - particle.size * 0.5;
particle.ax = speed;
particle.ay = speed;
- 印刷动画
前面咱们的打印机动画是整张图片从上到下慢慢平移出来,其实还有另外一种更好的印刷动画作用。
在 ParticleManage
中新增 my特点
,用于动画履行进展的衡量,即 动画履行进展
= my
/ 组件高度(400)
。
// 粒子动画履行间隔,总间隔是400
double my = 0;
这一次不再让图片从 -400
的方位开端移动,而是直接在当时方位显现
case Anim.printer2:
particle.cx = particle.x;
particle.cy = particle.y;
particle.ax = speed;
particle.ay = speed + 2;
break;
然后在粒子的更新 updateParticle
中,新增例子颜色透明度的判别。当粒子当时的 y 坐标 cy
< my
时,粒子透明度不为空。
if (anim == Anim.printer2) {
particle.color =
particle.color.withAlpha(particle.cy <= my ? 255 : 0);
}
最终咱们需求完善下粒子运动 completed
的逻辑判别,保证动画能够完全履行。
/// 粒子是否已移动到指定方位
bool isParticleCompleted(Particle particle) {
if (my > 0) {
return my >= particle.y &&
particle.cx == particle.x &&
particle.cy == particle.y;
} else {
return particle.cx == particle.x && particle.cy == particle.y;
}
}
完成作用:
- 粒子动画2
因为咱们在 ParticleManage
中新增了 my特点
,用于动画履行进展的衡量,因此咱们能够在本来的粒子动画中做出更详尽的动画作用。
咱们首先在 setParticleAnim
中新增 particleMotion2
动画的粒子特点设置
case Anim.particleMotion2: // 粒子运动2
particle.cx = particle.x - (random.nextDouble() * range - range ~/ 2);
particle.cy = particle.y + 100;
particle.ax = speed + random.nextDouble() * 10;
particle.ay = speed + random.nextDouble() * 5;
my = 50;
break;
然后在 updateParticle
中新增判别,只要在 my - 10
规模内的粒子才开端移动,一起新增粒子 alpha 判别,只要当粒子方位小于 my 时,才显现粒子
if (anim == Anim.particleMotion2) {
particle.color =
particle.color.withAlpha(particle.cy <= my ? 255 : 0);
if (particle.cy - my < 10) {
if (particle.cy > particle.y) {
particle.cy = max(particle.y, particle.cy - particle.ay);
} else if (particle.cy < particle.y) {
particle.cy = min(particle.y, particle.cy + particle.ay);
}
}
}
完成作用:
总结
今天首要完成粒子相册的相册功用,并且新增粒子颗粒度、运动速度、离散规模等具体特点配置,最终对原有的粒子动画进行整理概括,经过新增my特点来操控粒子的动画履行进展然后做出更详尽的粒子动画作用。
源码 particle_album
写下这篇文章的时分是我了的第四天,尽管我症状比较轻,但这个病毒依旧让我前3天十分难受。并且身边了的朋友根本都有症状,有的乃至一周还没缓过来。期望咱们不要小看这个病毒,能晚尽量晚点。