HarmonyOS实战开发-如何实现一个自定义抽奖圆形转盘

news2024/11/23 15:49:36

介绍

本篇Codelab是基于画布组件、显式动画,实现的一个自定义抽奖圆形转盘。包含如下功能:

  1. 通过画布组件Canvas,画出抽奖圆形转盘。
  2. 通过显式动画启动抽奖功能。
  3. 通过自定义弹窗弹出抽中的奖品。

相关概念

  • Stack组件:堆叠容器,子组件按照顺序依次入栈,后一个子组件覆盖前一个子组件。
  • Canvas:画布组件,用于自定义绘制图形。
  • CanvasRenderingContext2D对象:使用RenderingContext在Canvas组件上进行绘制,绘制对象可以是矩形、文本、图片等。
  • 显式动画:提供全局animateTo显式动画接口来指定由于闭包代码导致的状态变化插入过渡动效。
  • 自定义弹窗: 通过CustomDialogController类显示自定义弹窗。

环境搭建

软件要求

  • DevEco Studio版本:DevEco Studio 3.1 Release。
  • OpenHarmony SDK版本:API version 9。

硬件要求

  • 开发板类型:润和RK3568开发板。
  • OpenHarmony系统:3.2 Release。

环境搭建

完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:

  1. 获取OpenHarmony系统版本:标准系统解决方案(二进制)。以3.2 Release版本为例:

2.搭建烧录环境。

  1. 完成DevEco Device Tool的安装
  2. 完成RK3568开发板的烧录

3.搭建开发环境。

  1. 开始前请参考工具准备,完成DevEco Studio的安装和开发环境配置。
  2. 开发环境配置完成后,请参考使用工程向导创建工程(模板选择“Empty Ability”)。
  3. 工程创建完成后,选择使用真机进行调测。

代码结构解读

本篇Codelab只对核心代码进行讲解。

├──entry/src/main/ets	            // 代码区
│  ├──common
│  │  ├──constants
│  │  │  ├──ColorConstants.ets      // 颜色常量类
│  │  │  ├──CommonConstants.ets     // 公共常量类 
│  │  │  └──StyleConstants.ets      // 样式常量类 
│  │  └──utils
│  │     ├──CheckEmptyUtils.ets     // 数据判空工具类
│  │     └──Logger.ets              // 日志打印类
│  ├──entryability
│  │  └──EntryAbility.ts            // 程序入口类
│  ├──pages
│  │  └──CanvasPage.ets             // 主界面	
│  ├──view
│  │  └──PrizeDialog.ets            // 中奖信息弹窗类
│  └──viewmodel
│     ├──DrawModel.ets              // 画布相关方法类
│     ├──FillArcData.ets            // 绘制圆弧数据实体类
│     └──PrizeData.ets              // 中奖信息实体类
└──entry/src/main/resources         // 资源文件目录

构建主界面

在这个章节中,我们将完成示例主界面的开发,效果如图所示:

在绘制抽奖圆形转盘前,首先需要在CanvasPage.ets的aboutToAppear()方法中获取屏幕的宽高。

// CanvasPage.ets
// 获取context
let context = getContext(this);

aboutToAppear() {
  // 获取屏幕的宽高
  window.getLastWindow(context)
    .then((windowClass) => {
      let windowProperties = windowClass.getWindowProperties();
      this.screenWidth = px2vp(windowProperties.windowRect.width);
      this.screenHeight = px2vp(windowProperties.windowRect.height);
    })
    .catch((error: Error) => {
      Logger.error('Failed to obtain the window size. Cause: ' + JSON.stringify(error));
    })
}

在CanvasPage.ets布局界面中添加Canvas组件,在onReady()方法中进行绘制。

// CanvasPage.ets
private settings: RenderingContextSettings = new RenderingContextSettings(true);
private canvasContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);

Stack({ alignContent: Alignment.Center }) {
  Canvas(this.canvasContext)
    ...
    .onReady(() => {
      // 通过draw方法进行绘制
      this.drawModel.draw(this.canvasContext, this.screenWidth, this.screenHeight);
    })

  // 开始抽奖图片
  Image($r('app.media.ic_center'))
    ...
}
...

在DrawModel.ets中,通过draw()方法逐步进行自定义圆形抽奖转盘的绘制。

// DrawModel.ets
// 画抽奖圆形转盘
draw(canvasContext: CanvasRenderingContext2D, screenWidth: number, screenHeight: number) {
  if (CheckEmptyUtils.isEmptyObj(canvasContext)) {
    Logger.error('[DrawModel][draw] canvasContext is empty.');
    return;
  }
  this.canvasContext= canvasContext;
  this.screenWidth = screenWidth;
  this.canvasContext.clearRect(0, 0, this.screenWidth, screenHeight);
  // 将画布沿X、Y轴平移指定距离
  this.canvasContext.translate(this.screenWidth / CommonConstants.TWO,
    screenHeight / CommonConstants.TWO);
  // 画外部圆盘的花瓣
  this.drawFlower();
  // 画外部圆盘、小圈圈
  this.drawOutCircle();
  // 画内部圆盘
  this.drawInnerCircle();
  // 画内部扇形抽奖区域
  this.drawInnerArc();
  // 画内部扇形区域文字
  this.drawArcText();
  // 画内部扇形区域奖品对应的图片
  this.drawImage();
  this.canvasContext.translate(-this.screenWidth / CommonConstants.TWO,
    -screenHeight / CommonConstants.TWO);
}

画外部圆盘

画外部圆盘的花瓣:通过调用rotate()方法,将画布旋转指定角度。再通过调用save()和restore()方法,使画布保存最新的绘制状态。根据想要绘制的花瓣个数,改变旋转角度,循环画出花瓣效果。

// DrawModel.ets
// 画外部圆盘的花瓣
drawFlower() {
  let beginAngle = this.startAngle + this.avgAngle;
  const pointY = this.screenWidth * CommonConstants.FLOWER_POINT_Y_RATIOS;
  const radius = this.screenWidth * CommonConstants.FLOWER_RADIUS_RATIOS;
  const innerRadius = this.screenWidth * CommonConstants.FLOWER_INNER_RATIOS;
  for (let i = 0; i < CommonConstants.COUNT; i++) {
    this.canvasContext?.save();
    this.canvasContext?.rotate(beginAngle * Math.PI / CommonConstants.HALF_CIRCLE);
    this.fillArc(new FillArcData(0, -pointY, radius, 0, Math.PI * CommonConstants.TWO),
      ColorConstants.FLOWER_OUT_COLOR);

    this.fillArc(new FillArcData(0, -pointY, innerRadius, 0, Math.PI * CommonConstants.TWO),
      ColorConstants.FLOWER_INNER_COLOR);
    beginAngle += this.avgAngle;
    this.canvasContext?.restore();
  }
}

// 画弧线方法
fillArc(fillArcData: FillArcData, fillColor: string) {
  if (CheckEmptyUtils.isEmptyObj(fillArcData) || CheckEmptyUtils.isEmptyStr(fillColor)) {
    Logger.error('[DrawModel][fillArc] fillArcData or fillColor is empty.');
    return;
  }
  if (this.canvasContext !== undefined) {
    this.canvasContext.beginPath();
    this.canvasContext.fillStyle = fillColor;
    this.canvasContext.arc(fillArcData.x, fillArcData.y, fillArcData.radius,
      fillArcData.startAngle, fillArcData.endAngle);
    this.canvasContext.fill();
  }
}

画外部圆盘、圆盘边上的小圈圈:在指定的X、Y(0, 0)坐标处,画一个半径为this.screenWidth * CommonConstants.OUT_CIRCLE_RATIOS的圆形。接下来通过一个for循环,且角度每次递增CommonConstants.CIRCLE / CommonConstants.SMALL_CIRCLE_COUNT,来绘制圆环上的小圈圈。

// DrawModel.ets
drawOutCircle() {
  // 画外部圆盘
  this.fillArc(new FillArcData(0, 0, this.screenWidth * CommonConstants.OUT_CIRCLE_RATIOS, 0,
    Math.PI * CommonConstants.TWO), ColorConstants.OUT_CIRCLE_COLOR);

  let beginAngle = this.startAngle;
  // 画小圆圈
  for (let i = 0; i < CommonConstants.SMALL_CIRCLE_COUNT; i++) {
    this.canvasContext?.save();
    this.canvasContext?.rotate(beginAngle * Math.PI / CommonConstants.HALF_CIRCLE);
    this.fillArc(new FillArcData(this.screenWidth * CommonConstants.SMALL_CIRCLE_RATIOS, 0,
      CommonConstants.SMALL_CIRCLE_RADIUS, 0, Math.PI * CommonConstants.TWO),
      ColorConstants.WHITE_COLOR);
    beginAngle = beginAngle + CommonConstants.CIRCLE / CommonConstants.SMALL_CIRCLE_COUNT;
    this.canvasContext?.restore();
  }
}

画内部扇形抽奖区域

画内部圆盘、内部扇形抽奖区域:使用fillArc()方法绘制内部圆盘。通过一个for循环,角度每次递增this.avgAngle。然后不断更改弧线的起始弧度this.startAngle * Math.PI / CommonConstants.HALF_CIRCLE和弧线的终止弧度(this.startAngle + this.avgAngle) * Math.PI / CommonConstants.HALF_CIRCLE。最后用fill()方法对路径进行填充。

// DrawModel.ets
// 画内部圆盘
drawInnerCircle() {
  this.fillArc(new FillArcData(0, 0, this.screenWidth * CommonConstants.INNER_CIRCLE_RATIOS, 0,
    Math.PI * CommonConstants.TWO), ColorConstants.INNER_CIRCLE_COLOR);
  this.fillArc(new FillArcData(0, 0, this.screenWidth * CommonConstants.INNER_WHITE_CIRCLE_RATIOS, 0,
    Math.PI * CommonConstants.TWO), ColorConstants.WHITE_COLOR);
}

// 画内部扇形抽奖区域
drawInnerArc() {
  // 颜色集合
  let colors = [
    ColorConstants.ARC_PINK_COLOR, ColorConstants.ARC_YELLOW_COLOR,
    ColorConstants.ARC_GREEN_COLOR, ColorConstants.ARC_PINK_COLOR,
    ColorConstants.ARC_YELLOW_COLOR, ColorConstants.ARC_GREEN_COLOR
  ];
  let radius = this.screenWidth * CommonConstants.INNER_ARC_RATIOS;
  for (let i = 0; i < CommonConstants.COUNT; i++) {
    this.fillArc(new FillArcData(0, 0, radius, this.startAngle * Math.PI / CommonConstants.HALF_CIRCLE,
      (this.startAngle + this.avgAngle) * Math.PI / CommonConstants.HALF_CIRCLE), colors[i]);
    this.canvasContext?.lineTo(0, 0);
    this.canvasContext?.fill();
    this.startAngle += this.avgAngle;
  }
}

画内部抽奖区域文字:用for循环,通过drawCircularText()方法绘制每组文字。drawCircularText()方法接收三个参数,分别是字符串,起始弧度和终止弧度。绘制文本前需要设置每个字母占的弧度angleDecrement,然后设置水平和垂直的偏移量。垂直偏移量circleText.y - Math.sin(angle) * radius就是朝着圆心移动的距离;水平偏移circleText.x + Math.cos(angle) * radius,是为了让文字在当前弧范围文字居中。最后使用fillText()方法绘制文本。

// DrawModel.ets
// 画内部扇形区域文字
drawArcText() {
  if (this.canvasContext !== undefined) {
    this.canvasContext.textAlign = CommonConstants.TEXT_ALIGN;
    this.canvasContext.textBaseline = CommonConstants.TEXT_BASE_LINE;
    this.canvasContext.fillStyle = ColorConstants.TEXT_COLOR;
    this.canvasContext.font = StyleConstants.ARC_TEXT_SIZE + CommonConstants.CANVAS_FONT;
  }
  // 需要绘制的文本数组集合
  let textArrays = [
    $r('app.string.text_smile'),
    $r('app.string.text_hamburger'),
    $r('app.string.text_cake'),
    $r('app.string.text_smile'),
    $r('app.string.text_beer'),
    $r('app.string.text_watermelon')
  ];
  let arcTextStartAngle = CommonConstants.ARC_START_ANGLE;
  let arcTextEndAngle = CommonConstants.ARC_END_ANGLE;
  for (let i = 0; i < CommonConstants.COUNT; i++) {
    this.drawCircularText(this.getResourceString(textArrays[i]),
      (this.startAngle + arcTextStartAngle) * Math.PI / CommonConstants.HALF_CIRCLE,
      (this.startAngle + arcTextEndAngle) * Math.PI / CommonConstants.HALF_CIRCLE);
    this.startAngle += this.avgAngle;
  }
}

// 绘制圆弧文本
drawCircularText(textString: string, startAngle: number, endAngle: number) {
  if (CheckEmptyUtils.isEmptyStr(textString)) {
    Logger.error('[DrawModel][drawCircularText] textString is empty.');
    return;
  }
  class CircleText {
    x: number = 0;
    y: number = 0;
    radius: number = 0;
  }
  let circleText: CircleText = {
    x: 0,
    y: 0,
    radius: this.screenWidth * CommonConstants.INNER_ARC_RATIOS
  };
  // 圆的半径
  let radius = circleText.radius - circleText.radius / CommonConstants.COUNT;
  // 每个字母占的弧度
  let angleDecrement = (startAngle - endAngle) / (textString.length - 1);
  let angle = startAngle;
  let index = 0;
  let character: string;

  while (index < textString.length) {
    character = textString.charAt(index);
    this.canvasContext?.save();
    this.canvasContext?.beginPath();
    this.canvasContext?.translate(circleText.x + Math.cos(angle) * radius,
      circleText.y - Math.sin(angle) * radius);
    this.canvasContext?.rotate(Math.PI / CommonConstants.TWO - angle);
    this.canvasContext?.fillText(character, 0, 0);
    angle -= angleDecrement;
    index++;
    this.canvasContext?.restore();
  }
}

画内部抽奖区域文字对应图片:使用drawImage()方法绘制抽奖区域文字对应图片,该方法接收五个参数,分别是图片资源、绘制区域左上角的X和Y轴坐标、绘制区域的宽度和高度。

// DrawModel.ets
// 画内部扇形区域文字对应的图片
drawImage() {
  let beginAngle = this.startAngle;
  let imageSrc = [
    CommonConstants.WATERMELON_IMAGE_URL, CommonConstants.BEER_IMAGE_URL,
    CommonConstants.SMILE_IMAGE_URL, CommonConstants.CAKE_IMAGE_URL,
    CommonConstants.HAMBURG_IMAGE_URL, CommonConstants.SMILE_IMAGE_URL
  ];
  for (let i = 0; i < CommonConstants.COUNT; i++) {
    let image = new ImageBitmap(imageSrc[i]);
    this.canvasContext?.save();
    this.canvasContext?.rotate(beginAngle * Math.PI / CommonConstants.HALF_CIRCLE);
    this.canvasContext?.drawImage(image, this.screenWidth * CommonConstants.IMAGE_DX_RATIOS,
      this.screenWidth * CommonConstants.IMAGE_DY_RATIOS, CommonConstants.IMAGE_SIZE,
      CommonConstants.IMAGE_SIZE);
    beginAngle += this.avgAngle;
    this.canvasContext?.restore();
  }
}

实现抽奖功能

在CanvasPage.ets的Canvas组件中添加rotate属性,在Image组件中添加点击事件onClick。点击“开始抽奖”图片,圆形转盘开始转动抽奖。

// CanvasPage.ets
Stack({ alignContent: Alignment.Center }) {
  Canvas(this.canvasContext)
    ...
    .onReady(() => {
      this.drawModel.draw(this.canvasContext, this.screenWidth, this.screenHeight);
    })
    .rotate({
      x: 0,
      y: 0,
      z: 1,
      angle: this.rotateDegree,
      centerX: this.screenWidth / CommonConstants.TWO,
      centerY: this.screenHeight / CommonConstants.TWO
    })

  // 开始抽奖图片
  Image($r('app.media.ic_center'))
   ...
    .enabled(this.enableFlag)
    .onClick(() => {
      this.enableFlag = !this.enableFlag;
      // 开始抽奖
      this.startAnimator();
    })
}
...

圆形转盘开始转动抽奖:给转盘指定一个随机的转动角度randomAngle,保证每次转动的角度是随机的,即每次抽到的奖品也是随机的。动画结束后,转盘停止转动,抽奖结束,弹出抽中的奖品信息。

// CanvasPage.ets
dialogController: CustomDialogController = new CustomDialogController({
  builder: PrizeDialog({
    prizeData: $prizeData,
    enableFlag: $enableFlag
  }),
  autoCancel: false
});

// CanvasPage.ets
// 开始抽奖
startAnimator() {
  let randomAngle = Math.round(Math.random() * CommonConstants.CIRCLE);
  // 获取中奖信息
  this.prizeData = this.drawModel.showPrizeData(randomAngle);

  animateTo({
    duration: CommonConstants.DURATION,
    curve: Curve.Ease,
    delay: 0,
    iterations: 1,
    playMode: PlayMode.Normal,
    onFinish: () => {
      this.rotateDegree = CommonConstants.ANGLE - randomAngle;
      // 打开自定义弹窗,弹出抽奖信息
      this.dialogController.open();
    }
  }, () => {
    this.rotateDegree = CommonConstants.CIRCLE * CommonConstants.FIVE +
      CommonConstants.ANGLE - randomAngle;
  })
}

弹出抽中的奖品信息:抽奖结束后,弹出抽中的文本和图片信息,通过自定义弹窗实现。

// PrizeDialog.ets
@CustomDialog
export default struct PrizeDialog {
  @Link prizeData: PrizeData;
  @Link enableFlag: boolean;
  private controller?: CustomDialogController;

  build() {
    Column() {
      Image(this.prizeData.imageSrc)
        ...

      Text(this.prizeData.message)
        ...

      Text($r('app.string.text_confirm'))
        ...
        .onClick(() => {
          // 关闭自定义弹窗		
          this.controller?.close();
          this.enableFlag = !this.enableFlag;
        })
    }
    ...
  }
}

总结

您已经完成了本次Codelab的学习,并了解到以下知识点:

  1. 使用画布组件Canvas,画出抽奖圆形转盘。
  2. 使用显式动画启动抽奖功能。
  3. 使用自定义弹窗弹出抽中的奖品。

为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了一份全套最新版的HarmonyOS NEXT学习资源,获取完整版方式请点击→《HarmonyOS教学视频

HarmonyOS教学视频:语法ArkTS、TypeScript、ArkUI等.....视频教程

鸿蒙生态应用开发白皮书V2.0PDF:

获取完整版白皮书方式请点击→《鸿蒙生态应用开发白皮书V2.0PDF》

鸿蒙 (Harmony OS)开发学习手册

一、入门必看

  1. 应用开发导读(ArkTS)
  2. ……

二、HarmonyOS 概念

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全
  5. ........

三、如何快速入门?《做鸿蒙应用开发到底学习些啥?》

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

四、开发基础知识

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

五、基于ArkTS 开发

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

更多了解更多鸿蒙开发的相关知识可以参考:《鸿蒙 (Harmony OS)开发学习手册》

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

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

相关文章

统计XML文件内标签的种类和其数量及将xml格式转换为yolov5所需的txt格式

1、统计XML文件内标签的种类和其数量 对于自己标注的数据集&#xff0c;需在标注完成后需要对标注好的XML文件校验&#xff0c;下面是代码&#xff0c;只需将SrcDir换成需要统计的xml的文件夹即可。 import os from tqdm import tqdm import xml.dom.minidomdef ReadXml(File…

Day62-Nginx四层负载均衡及结合7层负载均衡实践

Day62-Nginx四层负载均衡及结合7层负载均衡实践 1. 什么是四层负载均衡&#xff1f;2. 四层负载均衡的常用场景3. 百万并发百亿PV大规模架构4. L4和L7的区别及常用软件。5. lvs、nginx、haproxy区别6. nginx四层负载均衡&#xff08;tcp/ip&#xff0c;ip:port&#xff09;7. n…

python多方式操作elasticsearch介绍

python多方式操作elasticsearch介绍 1. requests模块操作ES ​ requests 是一个 Python HTTP 库&#xff0c;它简化了发送 HTTP 请求和处理响应的过程。通过 requests 模块&#xff0c;开发人员可以轻松地与 Web 服务进行通信&#xff0c;包括获取网页内容、执行 API 请求等。…

JavaScript(一)---【js的两种导入方式、全局作用域、函数作用域、块作用域】

一.JavaScript介绍 1.1什么是JavaScript JavaScript简称“js”&#xff0c;js与java没有任何关系。 js是一种“轻量级、解释型、面向对象的脚本语言”。 二.JavaScript的两种导入方式 2.1内联式 在HTML文档中使用<script>标签直接引用。 <script>console.log…

跨越时空,启迪智慧:奇趣相机重塑儿童摄影与教育体验

【科技观察】近期&#xff0c;奇趣未来公司以其创新之作——“奇趣相机”微信小程序&#xff0c;强势进军儿童AI摄影市场。这款专为亚洲儿童量身定制的应用&#xff0c;凭借精准贴合亚洲儿童面部特征的AIGC大模型&#xff0c;以及丰富的摄影模板与场景设定&#xff0c;正在重新…

【每日跟读】常用英语500句(400~500)

【每日跟读】常用英语500句 Where can I buy a ticket? 在哪里能买到票&#xff1f; When is the next train? 下趟火车什么时候到&#xff1f; Thank you so much for helping me move yesterday. 非常感谢你昨天帮我搬家 I’m feeling a little under the weather toda…

与webpack类似的工具还有哪些?区别?

文章目录 一、模块化工具二、详细对比RollupParcelSnowpackVitewebpack 参考文献 一、模块化工具 模块化是一种处理复杂系统分解为更好的可管理模块的方式 可以用来分割&#xff0c;组织和打包应用。每个模块完成一个特定的子功能&#xff0c;所有的模块按某种方法组装起来&a…

吴恩达2022机器学习专项课程(一) 4.1 梯度下降

问题预览 梯度下降算法的作用是&#xff1f;梯度下降的过程&#xff1f;梯度下降和最小化成本函数的联系&#xff1f;所有的成本函数都是一个形状吗&#xff1f;在非凸形状中&#xff0c;梯度下降的更新过程是&#xff1f;在非凸形状中&#xff0c;不同的初值对最小化成本函数…

量化交易入门(三十二)什么是BIAS指标以及它的优缺点

BIAS&#xff0c;中文名称为“乖离率”&#xff0c;是量化交易中常用的一种技术指标&#xff0c;主要用于衡量价格偏离移动平均线的程度。下面我将从原理、优缺点和应用三个方面对BIAS指标进行详细讲解。 一、BIAS指标的原理 BIAS指标的计算公式为&#xff1a;BIAS(当前收盘价…

车载电子电器架构 —— 电气架构释放检查

车载电子电器架构 —— 电气架构释放检查 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明…

Android vehicle车辆属性新增demo

目录 前言一、Vehicle模块1.1 简介1.2 Vehicle框架1.3 主要功能和特点1.4 重要服务CarService1.4.1 简介1.4.2 组成1.4.3 启动时序1.4.4 作用 二、车辆属性新增demo2.1 CarPropertyService2.1.1 简介2.1.2 架构2.1.3 车辆属性 API2.1.4 CarPropertyService 初始化流程 2.2 App …

Sentinel原理及实践

Sentinel 是什么 Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件&#xff0c;主要以流量为切入点&#xff0c;从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。 为什么使用sentinel&…

RIP环境下的MGRE 综合实验

实验题目及要求&#xff1a; 1.R5为ISP&#xff0c;只能进行IP地址配置&#xff0c;其所有地址均配为公有IP地址 2.R1和R5间使用PPP的PAP认证&#xff0c;R5为主认证方; R2于R5之间使用PPP的chap认证&#xff0c;R5为主认证方&#xff1b; R3于R5之间使用HDLC封装。 3.R1/…

数据链路层之信道:数字通信的桥梁与守护者

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

LeetCode 双指针专题

11.盛最多水的容器 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明&#xff1a;你不…

Kubeflow文档1:介绍与架构

Kubeflow 2024/3/19版本的文档 此专栏用来展示相关的内容翻译&#xff0c;重点关注本地部署&#xff0c;关于运营商的方案&#xff0c;请自行查阅 文档地址https://www.kubeflow.org/docs/ 开始编辑时间&#xff1a;2024/3/27&#xff1b;最后编辑时间2024/3/27 Kubeflow文…

【Hello,PyQt】QTextEdit和QSplider

PyQt5 是一个强大的Python库&#xff0c;用于创建图形用户界面&#xff08;GUI&#xff09;。其中&#xff0c;QTextEdit 控件作为一个灵活多用的组件&#xff0c;常用于显示和编辑多行文本内容&#xff0c;支持丰富的格式设置和文本操作功能。另外&#xff0c;QSlider 控件是一…

java数据结构与算法刷题-----LeetCode1091. 二进制矩阵中的最短路径

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 广度优先双分裂蛇 广度优先双分裂蛇 双分裂蛇&#xff1a;是求二…

上门家政按摩H5小程序源码

《服务器环境配置》 1、服务器环境&#xff1a;CentOS7 宝塔 Nginx php 2、环境&#xff1a;PHP7.2 MySQL5.6 3、安装扩展&#xff1a;fileinfo、redis 《程序安装配置》 1、新建站点及数据库&#xff0c;然后申请创建SSL证书&#xff0c;配置到站点&#xff0c;开启强…

LeetCode Python - 84. 柱状图中最大的矩形

目录 题目描述解法方法一方法二 运行结果方法一方法二 题目描述 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 示例 1: 输入&#xff1a;heights …