Flutter动画详解第二篇之显式动画(Explicit Animations)

news2024/9/22 7:25:08

目录

前言

一、定义

1.AnimationController

1.常用属性

1. value

2. status

3. duration

2.常用方法

1.forward

2.reverse

3.repeat

4.stop

5. reset

6. animateTo(double target, {Duration? duration, Curve curve = Curves.linear})

7.animateBack(double target, {Duration? duration, Curve curve = Curves.linear})

8. addListener(VoidCallback listener)

10.addStatusListener(AnimationStatusListener listener)

11. removeStatusListener(AnimationStatusListener listener)

        移除状态监听器。

2.Animation

3.Tween

4.Listeners

5.Builders

二、常见的显示动画组件

1.RotationTransition       

2.FadeTransition

3.ScaleTransition

4.SlideTransition

三、自定义显式动画

四、参考文章


前言

    上一篇文章介绍了Flutter中的隐式动画的用法。

    我们会发现隐式动画使用起来非常的方便,我们只需要设置动画的旧值变化之后的值,Flutter会帮助我们完整动画的中间过程。Flutter中隐式动画的实现是全自动的。

     今天我们介绍下Flutter中的显式动画。

一、定义

        在Flutter中,显式动画(Explicit Animations)指的是那些需要你手动控制动画过程的动画。显式动画提供了更多的控制权,但也需要更多的代码和管理。显式动画通常涉及到以下几个核心组件:

  1. AnimationController
  2. Animation
  3. Tween
  4. Listeners
  5. Builders

            核心组件的详解如下:

1.AnimationController

        AnimationController是 Flutter 动画框架的核心部分之一,用于控制动画的播放、停止、前进、倒退等。它提供了丰富的 API 来管理和控制动画的行为。下面是 AnimationController的一些常用 API 及其解释:

1.常用属性

1. value

        当前动画的值。可以是 `double` 类型,表示动画当前的进度。

double currentValue = controller.value;

2. status

        当前动画的状态,是AnimationStatus枚举类型,可能的值有dismissed、forward、reverse 和 completed。

AnimationStatus currentStatus = controller.status;

3. duration

        动画的时长。

controller.duration = Duration(seconds: 2);

4. upperBound和 lowerBound

       动画的范围。

controller.upperBound = 1.0;
controller.lowerBound = 0.0;

2.常用方法

1.forward

        动画正向执行,从lowerBound到upperBound。

 controller.forward();

2.reverse

        动画反向执行,从upperBound到lowerBound

controller.reverse();

3.repeat

        重复执行动画,可以指定周期和是否反向。

 controller.repeat(reverse: true);

4.stop

        停止动画。

controller.stop();

5. reset

        重置动画到 `lowerBound`,并停止动画。

controller.reset();

6. animateTo(double target, {Duration? duration, Curve curve = Curves.linear})

        动画执行到指定值。

   controller.animateTo(0.5, duration: Duration(seconds: 1), curve: Curves.easeInOut);

7.animateBack(double target, {Duration? duration, Curve curve = Curves.linear})

        动画反向执行到指定值。

controller.animateBack(0.0, duration: Duration(seconds: 1), curve: Curves.easeInOut);

8. addListener(VoidCallback listener)

        添加监听器,每次动画值改变时调用。

controller.addListener(() {
     setState(() {
       // 更新UI
     });
   });

9. removeListener(VoidCallback listener)

        移除监听器。

controller.removeListener(listener);

10.addStatusListener(AnimationStatusListener listener)

        添加状态监听器,动画状态改变时调用。

controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        // 动画完成
      }
    });

11. removeStatusListener(AnimationStatusListener listener)
        移除状态监听器。

  controller.removeStatusListener(listener);

2.Animation

        在 Flutter 中,显式动画需要开发者手动管理动画的每个步骤,其中 Animation 类是核心组件之一。Animation 类本身是一个抽象类,它定义了动画的当前值和状态,并且可以被监听以响应动画的变化。通过 Animation 类,开发者可以访问动画的值并将其应用到 UI 元素上。

        在实际做动画的过程中,我们会用到各种各样的Animation。例如我们做缩放动画的时候,Animation的类型为double类型,渐变动画的时候,Animation可以表示颜色的动画,平移动画的时候,Animation可以表示平移的大小。

3.Tween

        定义动画的开始和结束值。常见的有 Tween<double>、ColorTween 等。

4.Listeners

        通过 addListener 和 addStatusListener 可以监听动画的每一帧和动画状态的变化。

5.Builders

        通过 AnimatedBuilder 或 CustomPainter 等将动画值应用到UI上。

二、常见的显示动画组件

        显示动画都以Transition结尾。常见的显示动画有RotateAnimation动画、FadeTransition、ScaleTransition、SlideTransition、AnimatedIcon。在显示动画中,我们通过AnimatedController控制动画的开始、暂停、重置、跳转、倒播等。

1.RotationTransition       

        RotationTransition主要用来做旋转动画。

        我们以下面的效果为例,看看如何使用RotationTransition动画。

        图1.RotationTransition动画

        我们要做一个不停旋转的FlutterLogo。

        首先我们设置FlutterLogo的大小为60。

        我们要做一个不停旋转的显式动画,因此我们需要再FlutterLogo的外面使用RotationTransition包裹起来。

代码如下:

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('RotateAnimation动画'),
      ),
      body: Center(
        child: RotationTransition(
          turns: controller,
          child: const FlutterLogo(
            size: 60,
          ),
        ),
      ),
    );
  }

        我们看一下RotatinTransition的定义:

  const RotationTransition({
    super.key,
    required Animation<double> turns,
    super.alignment = Alignment.center,
    super.filterQuality,
    super.child,
  }) : super(animation: turns, onTransform: _handleTurnsMatrix);

        这里必须要传递AnimationController对象。因此我们在定义AnimationController。

// 定义AnimationController
late AnimationController controller;

           为了让程序和手机的刷新频率保持一致,我们的Stateful后面要实现SingleTickerProviderStateMixin。

        代码如下:

class _RotateAnimationDemosState extends State<RotateAnimationDemos> with SingleTickerProviderStateMixin{
      // 定义AnimationController
  late AnimationController controller;
}

        然后我们Widget的初始化方法中,初始化我们的AnimationController。

        这里我们设置下动画的时长,vsync参数传this。表示当前app和手机刷新的频率保持一致。

  //初始化 AnimationController
  @override
  void initState() {
    super.initState();
    //vsync: 让程序和手机的刷新频率统一
    controller = AnimationController(duration: const Duration(seconds: 3), vsync: this);
  }

            OK.到这里之后,完整的代码如下:

import 'package:flutter/material.dart';

class RotateAnimationDemos extends StatefulWidget {
  const RotateAnimationDemos({super.key});

  @override
  State<RotateAnimationDemos> createState() => _RotateAnimationDemosState();
}

class _RotateAnimationDemosState extends State<RotateAnimationDemos>
    with SingleTickerProviderStateMixin {
  // 定义AnimationController
  late AnimationController controller;

  //初始化 AnimationController
  @override
  void initState() {
    super.initState();
    //vsync: 让程序和手机的刷新频率统一
    controller =
        AnimationController(duration: const Duration(seconds: 3), vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('RotateAnimation动画'),
      ),
      body: Center(
        child: RotationTransition(
          turns: controller,
          child: const FlutterLogo(
            size: 60,
          ),
        ),
      ),
    );
  }
}

        运行代码之后,页面加载出来了,但是FlutterLogo没有转起来。

        因为显式动画是手动控制动画的播放,因此我们还需要手动调用下AnimationController的repeat方法。

  //初始化 AnimationController
  @override
  void initState() {
    super.initState();
    //vsync: 让程序和手机的刷新频率统一
    controller = AnimationController(duration: const Duration(seconds: 3), vsync: this)..repeat();
  }

        再次运行,就发现FlutterLogo旋转起来了。

完整代码如下:

import 'package:flutter/material.dart';

class RotateAnimationDemos extends StatefulWidget {
  const RotateAnimationDemos({super.key});

  @override
  State<RotateAnimationDemos> createState() => _RotateAnimationDemosState();
}

class _RotateAnimationDemosState extends State<RotateAnimationDemos>
    with SingleTickerProviderStateMixin {
  // 定义AnimationController
  late AnimationController controller;

  //初始化 AnimationController
  @override
  void initState() {
    super.initState();
    //vsync: 让程序和手机的刷新频率统一
    controller = AnimationController(duration: const Duration(seconds: 3), vsync: this)..repeat();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('RotateAnimation动画'),
      ),
      body: Center(
        child: RotationTransition(
          turns: controller,
          child: const FlutterLogo(
            size: 60,
          ),
        ),
      ),
    );
  }
}

        上述调用了AnimationController的repeat方法,AnimationController还提供了forward、reverse、stop、reset等常用方法,它们的含义如下:

  1. forward:动画仅执行一次
  2. reverse:动画倒序执行一次
  3. stop:动画停止
  4. reset:动画重置
  5. repeat:重复的执行动画

        如果感兴趣,可以逐个调用这些方法看看效果。

2.FadeTransition

        FadeTransition用于制作透明度动画。

        FadeTransition和RotationTransition的用法基本差不多。

        下面的例子中,展示了使用FadeTransition制作动画的过程。

        图2.FadeTransition动画

        完整代码如下:

import 'package:flutter/material.dart';

class FadeAnimationDemos extends StatefulWidget {
  const FadeAnimationDemos({super.key});

  @override
  State<FadeAnimationDemos> createState() => _FadeAnimationDemosState();
}

class _FadeAnimationDemosState extends State<FadeAnimationDemos> with SingleTickerProviderStateMixin{
  // 定义AnimationController
  late AnimationController controller;

  //初始化 AnimationController
  @override
  void initState() {
    super.initState();
    //vsync: 让程序和手机的刷新频率统一
    controller = AnimationController(
        duration: const Duration(seconds: 1),
        vsync: this,
        lowerBound: 0.1,
         upperBound: 1.0,
    )..repeat();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('FadeTransition动画'),
      ),
      body: Center(
          child:FadeTransition(
            opacity: controller,
            child: const FlutterLogo(size: 200,),
          ),
      ),
    );
  }
}

3.ScaleTransition

        ScaleTransition用于制作缩放动画,依然可以使用AnimationController更加精准的控制动画的细节。

        图3.缩放动画

        完整代码如下:

import 'package:flutter/material.dart';

class ScaleTransitionDemos extends StatefulWidget {
  const ScaleTransitionDemos({super.key});

  @override
  State<ScaleTransitionDemos> createState() => _ScaleTransitionDemosState();
}

class _ScaleTransitionDemosState extends State<ScaleTransitionDemos> with SingleTickerProviderStateMixin{
  // 定义AnimationController
  late AnimationController controller;

  //初始化 AnimationController
  @override
  void initState() {
    super.initState();
    //vsync: 让程序和手机的刷新频率统一
    controller = AnimationController(
      duration: const Duration(seconds: 1),
      vsync: this,
      lowerBound: 0.1,
      upperBound: 1.0,
    )..repeat();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('ScaleTransition动画'),
      ),
      body: Center(
        child:ScaleTransition(
          scale: controller,
          child: Container(
            width: 100,
            height: 100,
            color: Colors.deepPurpleAccent,
          ),
        ),
      ),
    );
  }
}

4.SlideTransition

        SlideTransition用于做平移动画。

        下图是使用SlideTransition制作的平移动画。

图4.SlideTransition动画

        完整代码如下:

import 'package:flutter/material.dart';

class SlideTransitionDemos extends StatefulWidget {
  const SlideTransitionDemos({super.key});

  @override
  State<SlideTransitionDemos> createState() => _SlideTransitionDemosState();
}

class _SlideTransitionDemosState extends State<SlideTransitionDemos> with SingleTickerProviderStateMixin{
  // 定义AnimationController
  late AnimationController controller;

  //初始化 AnimationController
  @override
  void initState() {
    super.initState();
    //vsync: 让程序和手机的刷新频率统一
    controller = AnimationController(
      duration: const Duration(seconds: 1),
      vsync: this,
      lowerBound: 0.1,
      upperBound: 1.0,
    )..repeat();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SlideTransition动画'),
      ),
      body: Center(
        child:SlideTransition(
          position: controller.drive(Tween(begin: const Offset(0, 0), end: const Offset(0.5, 0))),
          child: const FlutterLogo(size: 200,),
        ),
      ),
    );
  }
}

        我们还可以使用AnimationController的drive方法修改动画的初始值。

        例如在下面的缩放动画代码中,开始的时候,长度和宽度都是从0.5倍开始,2倍结束。

        部分代码如下:

Center(
        child:ScaleTransition(
          scale: controller.drive(Tween(begin: 0.5, end: 2.0)),
          child: Container(
            width: 100,
            height: 100,
            color: Colors.deepPurpleAccent,
          ),
        ),
      )

        还可以调用AnimationController的chain方法叠加动画的曲线。

三、自定义显式动画

        这里我们看一下如何自定义显式动画。

        例如我们这里有一个透明度为0.9的Container组件,代码如下:

import 'package:flutter/material.dart';

class CustomExplicitPage extends StatefulWidget {
  const CustomExplicitPage({super.key});

  @override
  State<CustomExplicitPage> createState() => _CustomExplicitPageState();
}

class _CustomExplicitPageState extends State<CustomExplicitPage> with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('自定义显示动画'),
      ),
      body: Center(
        child: Opacity(
          opacity: 0.9,
          child: Container(
            alignment: Alignment.center,
            color: Colors.deepPurpleAccent,
            width: 250,
            height: 250,
            child: const Text('Hi',style: TextStyle(fontSize: 24),),
          ),
        ),
      ),
    );
  }
}

        我们看下如何使用AnimatedBuilder实现自定义显式动画。

        首先我们把需要制作动画的Widget的外层包裹一个AnimatedBuilder。builder函数内部返回要操作的Widget,把AnimatedController传给AnimatedBuilder的animation属性。

        部分代码如下:

Center(
        child: AnimatedBuilder(
          animation: _controller,
          builder: (BuildContext context, Widget? child) {
            return Opacity(
              opacity: 0.9,
              child: Container(
                alignment: Alignment.center,
                color: Colors.deepPurpleAccent,
                width: 250,
                height: 250,
                child: const Text('Hi',style: TextStyle(fontSize: 24),),
              ),
            );
          },
        ),
      )

             然后调用AnimationController的repeat方法,一个自定义的显式动画就实现了。

        完整代码如下:

import 'package:flutter/material.dart';

class CustomExplicitPage extends StatefulWidget {
  const CustomExplicitPage({super.key});

  @override
  State<CustomExplicitPage> createState() => _CustomExplicitPageState();
}

class _CustomExplicitPageState extends State<CustomExplicitPage> with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this,duration: const Duration(seconds: 2))..repeat();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('自定义显示动画'),
      ),
      body: Center(
        child: AnimatedBuilder(
          animation: _controller,
          builder: (BuildContext context, Widget? child) {
            return Opacity(
              opacity: _controller.value,
              child: Container(
                alignment: Alignment.center,
                color: Colors.deepPurpleAccent,
                width: 250,
                height: 250,
                child: const Text('Hi',style: TextStyle(fontSize: 24),),
              ),
            );
          },
        ),
      ),
    );
  }
}

        完整的效果如下:

        图5.自定义显式动画

四、参考文章

1.教程 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter

    

        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1932978.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

android R ext4 image打包脚本介绍

一、Android R打包指令使用介绍 &#xff08;1&#xff09;mkuserimg_mke2fs #./mkuserimg_mke2fs --help usage: mkuserimg_mke2fs [-h] [--android_sparse] [--journal_size JOURNAL_SIZE][--timestamp TIMESTAMP] [--fs_config FS_CONFIG][--product_out PRODUCT_OUT][--b…

Nginx、LNMP万字详解

目录 Nginx 特点 Nginx安装 添加Nginx服务 Nginx配置文件 全局配置 HTTP配置 状态统计页面 Nginx访问控制 授权用户 授权IP 虚拟主机 基于域名 测试 基于IP 测试 基于端口 测试 LNAMP 解析方式 LNMP转发php-fpm解析 Nginx代理LAMP解析 LNMP部署示例 实…

live555 rtsp服务器实战之doGetNextFrame

live555关于RTSP协议交互流程 live555的核心数据结构值之闭环双向链表 live555 rtsp服务器实战之createNewStreamSource live555 rtsp服务器实战之doGetNextFrame 注意&#xff1a;该篇文章可能有些绕&#xff0c;最好跟着文章追踪下源码&#xff0c;不了解源码可能就是天书…

message from server: “Too many connections“

theme: nico 你们好&#xff0c;我是金金金。 场景 启动服务时&#xff0c;报错如上&#xff1a;数据源拒绝建立连接&#xff0c;服务器发送消息&#xff1a;“连接过多” 排查 看报错信息提示的很明显了 查看MySQL 数据库中最大允许连接数的配置信息&#xff08;我mysql是部…

LabVIEW Communications LTE Application Framework 读书笔记

目录 硬件要求一台设备2台USRPUSRP-2974 示例项目的组件文件夹结构DL Host.gcompeNodeB Host.gcompUE Host.gcompBuildsCommonUSRP RIOLTE 操作模式DLeNodeBUE 项目组件单机双机UDP readUDP writeMAC TXMAC RXDL TX PHYDL RX PHYUL TX PHYUL RX PHYSINR calculationRate adapta…

python入门课程Pro(1)--数据结构及判断

数据结构及判断 第1课 复杂的多向选择1.if-elif-else2.if嵌套3.练习题&#xff08;1&#xff09;大招来了&#xff08;2&#xff09;奇数还是偶数&#xff08;3&#xff09;简洁代码 第2课 数据与判断小结1.变量2.格式化输出3.逻辑运算-或与非4.判断条件5.练习题&#xff08;1&…

基于 JAVA 的旅游网站设计与实现

点击下载源码 塞北村镇旅游网站设计 摘要 城市旅游产业的日新月异影响着村镇旅游产业的发展变化。网络、电子科技的迅猛前进同样牵动着旅游产业的快速成长。随着人们消费理念的不断发展变化&#xff0c;越来越多的人开始注意精神文明的追求&#xff0c;而不仅仅只是在意物质消…

[word] word如何编写公式? #微信#知识分享

word如何编写公式&#xff1f; word如何编写公式&#xff1f;Word中数学公式是经常会使用到的&#xff0c;若是要在文档中录入一些复杂的公式&#xff0c;要怎么做呢&#xff1f;接下来小编就来给大家讲一讲具体操作&#xff0c;一起看过来吧&#xff01; 方法一&#xff1a;…

RISC-V在线反汇编工具

RISC-V在线反汇编工具&#xff1a; https://luplab.gitlab.io/rvcodecjs/#q34179073&abifalse&isaAUTO 不过&#xff0c;似乎&#xff0c;只支持RV32I、RV64I、RV128I指令集&#xff1a;

Flutter热更新技术探索

一&#xff0c;需求背景&#xff1a; APP 发布到市场后&#xff0c;难免会遇到严重的 BUG 阻碍用户使用&#xff0c;因此有在不发布新版本 APP 的情况下使用热更新技术立即修复 BUG 需求。原生 APP&#xff08;例如&#xff1a;Android & IOS&#xff09;的热更新需求已经…

【精品资料】物业行业BI大数据解决方案(43页PPT)

引言&#xff1a;物业行业BI&#xff08;Business Intelligence&#xff0c;商业智能&#xff09;大数据解决方案是专为物业管理公司设计的一套综合性数据分析与决策支持系统。该解决方案旨在通过集成、处理、分析及可视化海量数据&#xff0c;帮助物业企业提升运营效率、优化资…

SCSA第七天

防火墙的可靠性 因为防火墙上不仅需要同步配置信息&#xff0c;还需要同步状态信息&#xff08;会话表等&#xff09;&#xff0c;所以&#xff0c;防火墙不能 像路由器那样单纯的靠动态协议来实现切换&#xff0c;需要用到双机热备技术。 1&#xff0c;双机 --- 目前双机热…

yearrecord——一个类似痕迹墙的React数据展示组件

介绍一下自己做的一个类似于力扣个人主页提交记录和GitHub主页贡献记录的React组件。 下图分别是力扣个人主页提交记录和GitHub个人主页的贡献记录&#xff0c;像这样类似痕迹墙的形式可以比较直观且高效得展示一段时间内得数据记录。 然而要从0实现这个功能还是有一些麻烦得…

构建gitlab远端服务器(check->build->test->deploy)

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录前言构建gitlab远端服务器一、步骤一:搭建gitlab的运行服务器【运维】1. 第一步:硬件服务器准备工作(1)选择合适的硬件和操作系统linux(2)安装必…

QT-RTSP相机监控视频流

QT-RTSP相机监控视频流 一、演示效果二、关键程序三、下载链接 一、演示效果 二、关键程序 #include "mainwindow.h"#include <QDebug>MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), m_settings("outSmart", "LiveWatcher&…

算法题目整合

文章目录 121. 小红的区间翻转142. 两个字符串的最小 ASCII 删除总和143. 最长同值路径139.完美数140. 可爱串141. 好二叉树 121. 小红的区间翻转 小红拿到了两个长度为 n 的数组 a 和 b&#xff0c;她仅可以执行一次以下翻转操作&#xff1a;选择a数组中的一个区间[i, j]&…

Apache AGE的MATCH子句

MATCH子句允许您在数据库中指定查询将搜索的模式。这是检索数据以在查询中使用的主要方法。 通常在MATCH子句之后会跟随一个WHERE子句&#xff0c;以添加用户定义的限制条件到匹配的模式中&#xff0c;以操纵返回的数据集。谓词是模式描述的一部分&#xff0c;不应被视为仅在匹…

OpenAI训练数据从哪里来、与苹果合作进展如何?“ChatGPT之母”最新回应

7月9日&#xff0c;美国约翰霍普金斯大学公布了对“ChatGPT之母”、OpenAI首席技术官米拉穆拉蒂&#xff08;Mira Murati&#xff09;的采访视频。这场采访时间是6月10日&#xff0c;访谈中&#xff0c;穆拉蒂不仅与主持人讨论了OpenAI与Apple的合作伙伴关系&#xff0c;还深入…

20.x86游戏实战-远线程注入的实现

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

mac M1 创建Mysql8.0容器

MySLQ8.0 拉取m1镜像 docker pull mysql:8.0创建挂载文件夹并且赋予权限 sudo chmod 777 /Users/zhao/software/dockerLocalData/mysql 创建容器并且挂载 docker run --name mysql_8 \-e MYSQL_ROOT_PASSWORDadmin \-v /Users/zhao/software/dockerLocalData/mysql/:/var/l…