flutter开发实战-hero实现图片预览功能extend_image

flutter开发实战-hero实现图片预览功能extend_image

在开发中,经常遇到需要图片预览,当feed中点击一个图片,开启预览,多个图片可以左右切换swiper,双击图片及手势进行缩放功能。
这个主要实现使用extend_image插件。在点击图片时候使用hero动画进行展示。

Hero简单使用,可以查看https://brucegwo.blog.csdn.net/article/details/134005601

hero实现图片预览功能效果图

在这里插入图片描述
在这里插入图片描述

一、图片GridView

在展示多张图片,使用GridView来展示。

GridView可以构建一个二维网格列表,其默认构造函数定义如下:

  GridView({
    Key? key,
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
    ScrollController? controller,
    bool? primary,
    ScrollPhysics? physics,
    bool shrinkWrap = false,
    EdgeInsetsGeometry? padding,
    required this.gridDelegate,  //下面解释
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
    double? cacheExtent, 
    List<Widget> children = const <Widget>[],
    ...
  })

SliverGridDelegate是一个抽象类,定义了GridView Layout相关接口,子类需要通过实现它们来实现具体的布局算法。

实现展示图片GridView

GridView.builder(
          gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
            maxCrossAxisExtent: 300,
            crossAxisSpacing: 10,
            mainAxisSpacing: 10,
          ),
          itemBuilder: (BuildContext context, int index) {

...

完整代码如下

class GridSimplePhotoViewDemo extends StatefulWidget {
  
  _GridSimplePhotoViewDemoState createState() =>
      _GridSimplePhotoViewDemoState();
}

class _GridSimplePhotoViewDemoState extends State<GridSimplePhotoViewDemo> {
  List<String> images = <String>[
    'https://photo.tuchong.com/14649482/f/601672690.jpg',
    'https://photo.tuchong.com/17325605/f/641585173.jpg',
    'https://photo.tuchong.com/3541468/f/256561232.jpg',
    'https://photo.tuchong.com/16709139/f/278778447.jpg',
    'This is an video',
    'https://photo.tuchong.com/5040418/f/43305517.jpg',
    'https://photo.tuchong.com/3019649/f/302699092.jpg'
  ];

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SimplePhotoView'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(10.0),
        child: GridView.builder(
          gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
            maxCrossAxisExtent: 300,
            crossAxisSpacing: 10,
            mainAxisSpacing: 10,
          ),
          itemBuilder: (BuildContext context, int index) {
            final String url = images[index];
            return GestureDetector(
              child: AspectRatio(
                aspectRatio: 1.0,
                child: Hero(
                  tag: url,
                  child: url == 'This is an video'
                      ? Container(
                          alignment: Alignment.center,
                          child: const Text('This is an video'),
                        )
                      : ExtendedImage.network(
                          url,
                          fit: BoxFit.cover,
                        ),
                ),
              ),
              onTap: () {
                Navigator.of(context).push(TransparentPageRoute(pageBuilder:
                    (BuildContext context, Animation<double> animation,
                        Animation<double> secondaryAnimation) {
                  return PicSwiper(
                    index: index,
                    pics: images,
                  );
                }));
              },
            );
          },
          itemCount: images.length,
        ),
      ),
    );
  }
}

二、跳转到Swiper的TransparentPageRoute

当点击跳转到新的页面的时候,可以使用TransparentPageRoute,该类继承与PageRouteBuilder,实现FadeTransition在点击图片展示预览图片的时候,通过渐隐渐显的方式跳转到下一个路由。

Widget _defaultTransitionsBuilder(
    BuildContext context,
    Animation<double> animation,
    Animation<double> secondaryAnimation,
    Widget child,
    ) {
  return FadeTransition(
    opacity: CurvedAnimation(
      parent: animation,
      curve: Curves.easeOut,
    ),
    child: child,
  );
}

完整代码如下

import 'package:flutter/material.dart';

/// Transparent Page Route
class TransparentPageRoute<T> extends PageRouteBuilder<T> {
  TransparentPageRoute({
    RouteSettings? settings,
    required RoutePageBuilder pageBuilder,
    RouteTransitionsBuilder transitionsBuilder = _defaultTransitionsBuilder,
    Duration transitionDuration = const Duration(milliseconds: 250),
    bool barrierDismissible = false,
    Color? barrierColor,
    String? barrierLabel,
    bool maintainState = true,
  }) : super(
    settings: settings,
    opaque: false,
    pageBuilder: pageBuilder,
    transitionsBuilder: transitionsBuilder,
    transitionDuration: transitionDuration,
    barrierDismissible: barrierDismissible,
    barrierColor: barrierColor,
    barrierLabel: barrierLabel,
    maintainState: maintainState,
  );
}

Widget _defaultTransitionsBuilder(
    BuildContext context,
    Animation<double> animation,
    Animation<double> secondaryAnimation,
    Widget child,
    ) {
  return FadeTransition(
    opacity: CurvedAnimation(
      parent: animation,
      curve: Curves.easeOut,
    ),
    child: child,
  );
}

三、使用extend_image

在pubspec.yaml引入extend_image

  # extended_image
  extended_image: ^7.0.2

当点击图片的时候,传入多张图片,定位到当前的index,多个图片可以左右切换Swiper。这里使用到了ExtendedImageGesturePageView。ExtendedImageGesturePageView与PageView类似,它是为显示缩放/平移图像而设计的。
如果您已经缓存了手势,请记住在正确的时间调用clearGestureDetailsCache()方法。(例如,页面视图页面被丢弃)

ExtendedImageGesturePageView属性

  • cacheGesture 保存手势状态(例如在ExtendedImageGesturePageView中,向后滚动时手势状态不会改变),记住clearGestureDetailsCache
  • inPageView 是否在ExtendedImageGesturePageView中

使用示例

ExtendedImageGesturePageView.builder(
  itemBuilder: (BuildContext context, int index) {
    var item = widget.pics[index].picUrl;
    Widget image = ExtendedImage.network(
      item,
      fit: BoxFit.contain,
      mode: ExtendedImageMode.gesture,
      gestureConfig: GestureConfig(
        inPageView: true, initialScale: 1.0,
        //you can cache gesture state even though page view page change.
        //remember call clearGestureDetailsCache() method at the right time.(for example,this page dispose)
        cacheGesture: false
      ),
    );
    image = Container(
      child: image,
      padding: EdgeInsets.all(5.0),
    );
    if (index == currentIndex) {
      return Hero(
        tag: item + index.toString(),
        child: image,
      );
    } else {
      return image;
    }
  },
  itemCount: widget.pics.length,
  onPageChanged: (int index) {
    currentIndex = index;
    rebuild.add(index);
  },
  controller: PageController(
    initialPage: currentIndex,
  ),
  scrollDirection: Axis.horizontal,
)

四、使用hero_widget

当点击图片,实现hero_widget实现hero动画来实现图片预览。
使用Flutter的Hero widget创建hero动画。 将hero从一个路由飞到另一个路由。 将hero 的形状从圆形转换为矩形,同时将其从一个路由飞到另一个路由的过程中进行动画处理。

这里使用的hero_widget完整代码如下

import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';

/// make hero better when slide out
class HeroWidget extends StatefulWidget {
  const HeroWidget({
    required this.child,
    required this.tag,
    required this.slidePagekey,
    this.slideType = SlideType.onlyImage,
  });
  final Widget child;
  final SlideType slideType;
  final Object tag;
  final GlobalKey<ExtendedImageSlidePageState> slidePagekey;
  
  _HeroWidgetState createState() => _HeroWidgetState();
}

class _HeroWidgetState extends State<HeroWidget> {
  RectTween? _rectTween;
  
  Widget build(BuildContext context) {
    return Hero(
      tag: widget.tag,
      createRectTween: (Rect? begin, Rect? end) {
        _rectTween = RectTween(begin: begin, end: end);
        return _rectTween!;
      },
      // make hero better when slide out
      flightShuttleBuilder: (BuildContext flightContext,
          Animation<double> animation,
          HeroFlightDirection flightDirection,
          BuildContext fromHeroContext,
          BuildContext toHeroContext) {
        // make hero more smoothly
        final Hero hero = (flightDirection == HeroFlightDirection.pop
            ? fromHeroContext.widget
            : toHeroContext.widget) as Hero;
        if (_rectTween == null) {
          return hero;
        }

        if (flightDirection == HeroFlightDirection.pop) {
          final bool fixTransform = widget.slideType == SlideType.onlyImage &&
              (widget.slidePagekey.currentState!.offset != Offset.zero ||
                  widget.slidePagekey.currentState!.scale != 1.0);

          final Widget toHeroWidget = (toHeroContext.widget as Hero).child;
          return AnimatedBuilder(
            animation: animation,
            builder: (BuildContext buildContext, Widget? child) {
              Widget animatedBuilderChild = hero.child;

              // make hero more smoothly
              animatedBuilderChild = Stack(
                clipBehavior: Clip.antiAlias,
                alignment: Alignment.center,
                children: <Widget>[
                  Opacity(
                    opacity: 1 - animation.value,
                    child: UnconstrainedBox(
                      child: SizedBox(
                        width: _rectTween!.begin!.width,
                        height: _rectTween!.begin!.height,
                        child: toHeroWidget,
                      ),
                    ),
                  ),
                  Opacity(
                    opacity: animation.value,
                    child: animatedBuilderChild,
                  )
                ],
              );

              // fix transform when slide out
              if (fixTransform) {
                final Tween<Offset> offsetTween = Tween<Offset>(
                    begin: Offset.zero,
                    end: widget.slidePagekey.currentState!.offset);

                final Tween<double> scaleTween = Tween<double>(
                    begin: 1.0, end: widget.slidePagekey.currentState!.scale);
                animatedBuilderChild = Transform.translate(
                  offset: offsetTween.evaluate(animation),
                  child: Transform.scale(
                    scale: scaleTween.evaluate(animation),
                    child: animatedBuilderChild,
                  ),
                );
              }

              return animatedBuilderChild;
            },
          );
        }
        return hero.child;
      },
      child: widget.child,
    );
  }
}

五、使用Pic_Swiper

在swiper左右切换功能,使用ExtendedImageGesturePageView来实现切换功能,双击图片及手势进行缩放功能。

完整代码如下

typedef DoubleClickAnimationListener = void Function();

class PicSwiper extends StatefulWidget {
  const PicSwiper({
    super.key,
    this.index,
    this.pics,
  });

  final int? index;
  final List<String>? pics;

  
  _PicSwiperState createState() => _PicSwiperState();
}

class _PicSwiperState extends State<PicSwiper> with TickerProviderStateMixin {
  final StreamController<int> rebuildIndex = StreamController<int>.broadcast();
  final StreamController<bool> rebuildSwiper =
      StreamController<bool>.broadcast();
  final StreamController<double> rebuildDetail =
      StreamController<double>.broadcast();
  late AnimationController _doubleClickAnimationController;
  late AnimationController _slideEndAnimationController;
  late Animation<double> _slideEndAnimation;
  Animation<double>? _doubleClickAnimation;
  late DoubleClickAnimationListener _doubleClickAnimationListener;
  List<double> doubleTapScales = <double>[1.0, 2.0];
  GlobalKey<ExtendedImageSlidePageState> slidePagekey =
      GlobalKey<ExtendedImageSlidePageState>();
  int? _currentIndex = 0;
  bool _showSwiper = true;
  double _imageDetailY = 0;
  Rect? imageDRect;

  
  Widget build(BuildContext context) {
    final Size size = MediaQuery.of(context).size;
    double statusBarHeight = MediaQuery.of(context).padding.top;

    imageDRect = Offset.zero & size;
    Widget result = Material(
      color: Colors.transparent,
      shadowColor: Colors.transparent,
      child: Stack(
        fit: StackFit.expand,
        children: <Widget>[
          ExtendedImageGesturePageView.builder(
            controller: ExtendedPageController(
              initialPage: widget.index!,
              pageSpacing: 50,
              shouldIgnorePointerWhenScrolling: false,
            ),
            scrollDirection: Axis.horizontal,
            physics: const BouncingScrollPhysics(),
            canScrollPage: (GestureDetails? gestureDetails) {
              return _imageDetailY >= 0;
            },
            itemBuilder: (BuildContext context, int index) {
              final String item = widget.pics![index];

              Widget image = ExtendedImage.network(
                item,
                fit: BoxFit.contain,
                enableSlideOutPage: true,
                mode: ExtendedImageMode.gesture,
                imageCacheName: 'CropImage',
                //layoutInsets: EdgeInsets.all(20),
                initGestureConfigHandler: (ExtendedImageState state) {
                  double? initialScale = 1.0;

                  if (state.extendedImageInfo != null) {
                    initialScale = initScale(
                        size: size,
                        initialScale: initialScale,
                        imageSize: Size(
                            state.extendedImageInfo!.image.width.toDouble(),
                            state.extendedImageInfo!.image.height.toDouble()));
                  }
                  return GestureConfig(
                    inPageView: true,
                    initialScale: initialScale!,
                    maxScale: max(initialScale, 5.0),
                    animationMaxScale: max(initialScale, 5.0),
                    initialAlignment: InitialAlignment.center,
                    //you can cache gesture state even though page view page change.
                    //remember call clearGestureDetailsCache() method at the right time.(for example,this page dispose)
                    cacheGesture: false,
                  );
                },
                onDoubleTap: (ExtendedImageGestureState state) {
                  ///you can use define pointerDownPosition as you can,
                  ///default value is double tap pointer down postion.
                  final Offset? pointerDownPosition = state.pointerDownPosition;
                  final double? begin = state.gestureDetails!.totalScale;
                  double end;

                  //remove old
                  _doubleClickAnimation
                      ?.removeListener(_doubleClickAnimationListener);

                  //stop pre
                  _doubleClickAnimationController.stop();

                  //reset to use
                  _doubleClickAnimationController.reset();

                  if (begin == doubleTapScales[0]) {
                    end = doubleTapScales[1];
                  } else {
                    end = doubleTapScales[0];
                  }

                  _doubleClickAnimationListener = () {
                    //print(_animation.value);
                    state.handleDoubleTap(
                        scale: _doubleClickAnimation!.value,
                        doubleTapPosition: pointerDownPosition);
                  };
                  _doubleClickAnimation = _doubleClickAnimationController
                      .drive(Tween<double>(begin: begin, end: end));

                  _doubleClickAnimation!
                      .addListener(_doubleClickAnimationListener);

                  _doubleClickAnimationController.forward();
                },
                loadStateChanged: (ExtendedImageState state) {
                  if (state.extendedImageLoadState == LoadState.completed) {
                    return StreamBuilder<double>(
                      builder:
                          (BuildContext context, AsyncSnapshot<double> data) {
                        return ExtendedImageGesture(
                          state,
                          imageBuilder: (Widget image) {
                            return Stack(
                              children: <Widget>[
                                Positioned.fill(
                                  child: image,
                                ),
                              ],
                            );
                          },
                        );
                      },
                      initialData: _imageDetailY,
                      stream: rebuildDetail.stream,
                    );
                  }
                  return null;
                },
              );

              image = HeroWidget(
                tag: item,
                slideType: SlideType.onlyImage,
                slidePagekey: slidePagekey,
                child: image,
              );

              image = GestureDetector(
                child: image,
                onTap: () {
                  slidePagekey.currentState!.popPage();
                  Navigator.pop(context);
                },
              );

              return image;
            },
            itemCount: widget.pics!.length,
            onPageChanged: (int index) {
              _currentIndex = index;
              rebuildIndex.add(index);
              if (_imageDetailY != 0) {
                _imageDetailY = 0;
                rebuildDetail.sink.add(_imageDetailY);
              }
              _showSwiper = true;
              rebuildSwiper.add(_showSwiper);
            },
          ),
          StreamBuilder<bool>(
            builder: (BuildContext c, AsyncSnapshot<bool> d) {
              if (d.data == null || !d.data!) {
                return Container();
              }

              return Positioned(
                top: statusBarHeight,
                left: 0.0,
                right: 0.0,
                child: MySwiperPlugin(widget.pics, _currentIndex, rebuildIndex),
              );
            },
            initialData: true,
            stream: rebuildSwiper.stream,
          )
        ],
      ),
    );

    result = ExtendedImageSlidePage(
      key: slidePagekey,
      child: result,
      slideAxis: SlideAxis.vertical,
      slideType: SlideType.onlyImage,
      slideScaleHandler: (
        Offset offset, {
        ExtendedImageSlidePageState? state,
      }) {
        return null;
      },
      slideOffsetHandler: (
        Offset offset, {
        ExtendedImageSlidePageState? state,
      }) {
        return null;
      },
      slideEndHandler: (
        Offset offset, {
        ExtendedImageSlidePageState? state,
        ScaleEndDetails? details,
      }) {
        return null;
      },
      onSlidingPage: (ExtendedImageSlidePageState state) {
        ///you can change other widgets' state on page as you want
        ///base on offset/isSliding etc
        //var offset= state.offset;
        final bool showSwiper = !state.isSliding;
        if (showSwiper != _showSwiper) {
          // do not setState directly here, the image state will change,
          // you should only notify the widgets which are needed to change
          // setState(() {
          // _showSwiper = showSwiper;
          // });

          _showSwiper = showSwiper;
          rebuildSwiper.add(_showSwiper);
        }
      },
    );

    return result;
  }

  
  void dispose() {
    rebuildIndex.close();
    rebuildSwiper.close();
    rebuildDetail.close();
    _doubleClickAnimationController.dispose();
    _slideEndAnimationController.dispose();
    clearGestureDetailsCache();
    //cancelToken?.cancel();
    super.dispose();
  }

  
  void initState() {
    super.initState();
    _currentIndex = widget.index;
    _doubleClickAnimationController = AnimationController(
        duration: const Duration(milliseconds: 150), vsync: this);

    _slideEndAnimationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 150),
    );
    _slideEndAnimationController.addListener(() {
      _imageDetailY = _slideEndAnimation.value;
      if (_imageDetailY == 0) {
        _showSwiper = true;
        rebuildSwiper.add(_showSwiper);
      }
      rebuildDetail.sink.add(_imageDetailY);
    });
  }
}

class MySwiperPlugin extends StatelessWidget {
  const MySwiperPlugin(this.pics, this.index, this.reBuild);

  final List<String>? pics;
  final int? index;
  final StreamController<int> reBuild;

  
  Widget build(BuildContext context) {
    return StreamBuilder<int>(
      builder: (BuildContext context, AsyncSnapshot<int> data) {
        return DefaultTextStyle(
          style: const TextStyle(color: Colors.blue),
          child: Container(
            height: 50.0,
            width: double.infinity,
            // color: Colors.grey.withOpacity(0.2),
            child: Row(
              children: <Widget>[
                Container(
                  width: 10.0,
                ),
                Text(
                  '${data.data! + 1}',
                ),
                Text(
                  ' / ${pics!.length}',
                ),
                const SizedBox(
                  width: 10.0,
                ),
                const SizedBox(
                  width: 10.0,
                ),
                if (!kIsWeb)
                  GestureDetector(
                    child: Container(
                      padding: const EdgeInsets.only(right: 10.0),
                      alignment: Alignment.center,
                      child: const Text(
                        'Save',
                        style: TextStyle(fontSize: 16.0, color: Colors.blue),
                      ),
                    ),
                    onTap: () {
                      // saveNetworkImageToPhoto(pics![index!].picUrl)
                      //     .then((bool done) {
                      //   showToast(done ? 'save succeed' : 'save failed',
                      //       position: const ToastPosition(
                      //           align: Alignment.topCenter));
                      // });
                    },
                  ),
              ],
            ),
          ),
        );
      },
      initialData: index,
      stream: reBuild.stream,
    );
  }
}

class ImageDetailInfo {
  ImageDetailInfo({
    required this.imageDRect,
    required this.pageSize,
    required this.imageInfo,
  });

  final GlobalKey<State<StatefulWidget>> key = GlobalKey<State>();

  final Rect imageDRect;

  final Size pageSize;

  final ImageInfo imageInfo;

  double? _maxImageDetailY;

  double get imageBottom => imageDRect.bottom - 20;

  double get maxImageDetailY {
    try {
      //
      return _maxImageDetailY ??= max(
          key.currentContext!.size!.height - (pageSize.height - imageBottom),
          0.1);
    } catch (e) {
      //currentContext is not ready
      return 100.0;
    }
  }
}

使用过程中的util

import 'package:extended_image/extended_image.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';

///
///  create by zmtzawqlp on 2020/1/31
///
double? initScale({
  required Size imageSize,
  required Size size,
  double? initialScale,
}) {
  final double n1 = imageSize.height / imageSize.width;
  final double n2 = size.height / size.width;
  if (n1 > n2) {
    final FittedSizes fittedSizes =
        applyBoxFit(BoxFit.contain, imageSize, size);
    //final Size sourceSize = fittedSizes.source;
    final Size destinationSize = fittedSizes.destination;
    return size.width / destinationSize.width;
  } else if (n1 / n2 < 1 / 4) {
    final FittedSizes fittedSizes =
        applyBoxFit(BoxFit.contain, imageSize, size);
    //final Size sourceSize = fittedSizes.source;
    final Size destinationSize = fittedSizes.destination;
    return size.height / destinationSize.height;
  }

  return initialScale;
}

效果视频

六、小结

flutter开发实战-hero实现图片预览功能extend_image。描述可能不太准确,请见谅。

学习记录,每天不停进步。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

达梦:开启sql日志记录

前言 开启sql日志记录&#xff0c;可协助排查定位数据库问题。生产开启会有一定的性能消耗&#xff0c;建议打开 SQL 日志异步刷盘功能 1.配置sqllog.ini文件 sqllog.ini 用于 SQL 日志的配置&#xff0c;当且仅当 INI 参数 SVR_LOG1 时使用。 运行中的数据库实例&#xff0c;可…

Go学习第十一章——协程goroutine与管道channel

Go协程goroutine与管道channel 1 协程goroutine1.1 基本介绍1.2 快速入门1.3 调度模型&#xff1a;MPG模式介绍1.4 设置cpu数1.5 协程资源竞争问题1.6 解决协程并发方案 2 管道channel2.1 基本介绍2.2 快速入门2.3 管道的关闭和遍历2.4 管道和协程的结合2.5 声明 只读/只写 的管…

GZ035 5G组网与运维赛题第2套

2023年全国职业院校技能大赛 GZ035 5G组网与运维赛项(高职组) 赛题第2套 一、竞赛须知 1.竞赛内容分布 竞赛模块1--5G公共网络规划部署与开通(35分) 子任务1:5G公共网络部署与调试(15分) 子任务2:5G室内与室外站点建设(20分) 竞赛模块2--5G公共网络运维与优化(…

Codeforces Round 905 (Div. 3)ABCDEF

Codeforces Round 905 (Div. 3) 目录 A. Morning题意思路核心代码 B. Chemistry题意思路核心代码 C. Raspberries题意思路核心代码 D. In Love题意思路核心代码 E. Look Back题意思路核心代码 A. Morning 题意 从一开始&#xff0c;每一次操作可以选择当前的数字打印或者是移…

Vue3 + Tsx 集成 ace-editor编辑器

Ace Editor介绍 Ace Editor&#xff08;全名&#xff1a;Ajax.org Cloud9 Editor&#xff09;是一个开源的代码编辑器&#xff0c;旨在提供强大的代码编辑功能&#xff0c;通常用于构建基于Web的代码编辑应用程序。它最初由Cloud9 IDE开发&#xff0c;现在由开源社区维护。 主…

C++ 左值、右值、左值引用以及右值引用

一、左值和右值 将亡值 1.左值 左值是一个表示数据的表达式&#xff0c;比如&#xff1a;变量名、解引用的指针变量。一般地&#xff0c;我们可以获取它的地址和对它赋值&#xff0c;但被 const 修饰后的左值&#xff08;常性&#xff09;&#xff0c;不能给它赋值&#xff0…

【安装tensorflow-CPU版本】

一、安装目的二、安装过程三、总结 一、安装目的 使自己的jupyter能用tensorflow 二、安装过程 首先打开anaconda prompt 接着输入conda list 查看自己是否安装了tensorflow 在 Python 中使用 pip 工具来升级 pip 自身并指定了使用清华大学的镜像源进行安装 python -m pip …

手写Vue渲染器render函数

使用js对象来描述UI更加的灵活。“这种对象”在vue框架中被称为虚拟DOM&#xff0c;渲染函数内部可以创建虚拟DOM&#xff0c;然后vue.js可以将其内容进行渲染。 1.渲染器的介绍 渲染器的作用就是把虚拟DOM渲染为真实DOM 思考下&#xff0c;我们有一个虚拟 DOM&#xff0c;如…

K8s概念汇总-笔记

目录 1.Master 1.1在Master上运⾏着以下关键进程 2.什么是Node? 1.2在每个Node上都运⾏着以下关键进程 3.什么是 Pod ? 4. 什么是Label &#xff1f; 5.Replication Controller 6.Deployment 6.1Deployment的典型场景&#xff1a; 7.Horizontal Pod Autoscaler TODO…

【计算机毕设小程序案例】基于SpringBoot的小演员招募小程序

前言&#xff1a;我是IT源码社&#xff0c;从事计算机开发行业数年&#xff0c;专注Java领域&#xff0c;专业提供程序设计开发、源码分享、技术指导讲解、定制和毕业设计服务 &#x1f449;IT源码社-SpringBoot优质案例推荐&#x1f448; &#x1f449;IT源码社-小程序优质案例…

Linux mkdir命令:创建目录(文件夹)

mkdir 命令&#xff0c;是 make directories 的缩写&#xff0c;用于创建新目录&#xff0c;此命令所有用户都可以使用。mkdir 命令的基本格式为&#xff1a; [rootlocalhost ~]# mkdir [-mp] 目录名 -m 选项用于手动配置所创建目录的权限&#xff0c;而不再使用默认权限。 -p…

[ubuntu系统下的文本编辑器nano,vim,gedit,文件使用,以及版本更新问题]

文本编辑器概要 在Ubuntu系统下&#xff0c;有许多文本编辑器可供选择&#xff0c;每个编辑器都有其独特的特性和用途。以下是一些常见的文本编辑器&#xff1a; Gedit&#xff1a; 这是Ubuntu默认的文本编辑器&#xff0c;它简单易用&#xff0c;适合基本的文本编辑任务。 安…

银河麒麟v10x86或者arm离线安装服务

银河麒麟v10x86或者arm离线安装服务 最近有个项目&#xff0c;甲方的服务器用的全是国产化服务器银河麒麟&#xff0c;架构是x86的然后也无法连接外网&#xff0c;需要离线安装服务正常思路就是找到离线安装的包&#xff0c;然后拷贝到现场的服务器中进行安装所以问题就在于如…

墨西哥专线相关问题快问快答

随着全球贸易的不断发展&#xff0c;越来越多的企业在寻求更便捷、高效的物流解决方案。墨西哥专线作为一种跨境物流方式&#xff0c;受到了越来越多企业的关注。本文将为您解答关于墨西哥专线的相关问题&#xff0c;帮助您更好地了解和运用这一物流方式。 一、墨西哥专线是什么…

Prompt设计与大语言模型微调

本文主要介绍了Prompt设计、大语言模型SFT和LLM在手机天猫AI导购助理项目应用。 ChatGPT基本原理 “会说话的AI”&#xff0c;“智能体” 简单概括成以下几个步骤&#xff1a; 预处理文本&#xff1a;ChatGPT的输入文本需要进行预处理。输入编码&#xff1a;ChatGPT将经过预处理…

MySQL 概述 数据库表操作 数据增删改

目录 MySQL概述前言安装与配置MySQL登录与卸载 数据模型概述SQL简介SQL通用语法简介SQL分类 数据库设计(数据库操作)-DDL数据库操作查询数据库 show databases、select database()创建数据库 create database使用数据库 use删除数据库 drop database 图形化工具连接数据库操作数…

zk-Bench:SNARKs性能对比评估工具

1. 引言 JENS ERNSTBERGER等人2023年论文《zk-Bench: A Toolset for Comparative Evaluation and Performance Benchmarking of SNARKs》。 zk-Bench&#xff0c;定位为&#xff1a; 定位为首个公钥密码学性能评估基准测试框架和工具&#xff0c;重点关注通用ZKP系统的实测评…

【Vue3-Flask-BS架构Web应用】实践笔记1-使用一个bat脚本自动化完整部署环境

前言 近年来&#xff0c;Web开发已经成为计算机科学领域中最热门和多产的领域之一。Python和Vue.js是两个备受欢迎的工具&#xff0c;用于构建现代Web应用程序。在本教程中&#xff0c;我们将探索如何使用这两个工具来创建一个完整的Web项目。我们将完成从安装Python和Vue.js到…

ESP8266,手机与电脑之间的TCP通讯

电脑端运行通讯猫调试助手,作为服务端: 电脑端 电脑的IP地址是: 192.168.2.232 手机与电脑之间的TCP通讯 手机端运行网络调试精灵,作为客户端: 手机端 如果从手机端点击"发送"按钮,则也会将"ghhh东方红广场"几个字发送到电脑上(服务端). ESP8266作为客户…

存储器概述

一、存储系统基本概念