绘制折线上一小节我们已经学过了,利用 moveTo、多个 lineTo、stroke 这三个方法就可以做到。本小节我们先用之前学过的内容绘制一个矩形。先看整体案例,请在firefox浏览器中查看本案例。1414如果你使用的是 firefox 浏览器,你会看到这样的效果:chrome浏览器下的效果是这样的:两个图一对比应该可以看出来,在firefox下,用上面代码绘制的矩形是有一定问题的,左上角会有一个明显的缺口,这是由于绘制的线条宽度超过了 1px,如果绘制 1px 宽的线条,则不会有这个问题。既然上面代码绘制的矩形不是很完美,那有没有比这个更好的绘制方案呢?这就要用到我们今天学习的新方法 closePath()。closePath() 方法用于创建从笔触当前点到开始点的路径。先看整体案例,依然是在 firefox 浏览器下查看效果。1415运行结果:
canvas 中和线条有关的属性有五个:lineWidth 线条宽度属性lineWidth 定义线的宽度(默认值为1.0)。strokeStyle 线条描边属性strokeStyle 定义线和形状边框的颜色和样式。lineCap 线条帽子属性lineCap 定义上下文中线的端点,可以有以下3个值,butt、round 和 square。lineJoin 线条拐角属性lineJoin 定义两条线相交产生的拐角,可将其称为连接,默认在拐角处创建一个填充的三角形,lineJoin 有三个值:miter、bevel 和 round。miterLimit 线条斜接长度限制属性miterLimit 属性需要和 lineJoin 属性配合使用,而且只有当 lineJoin=miter 时 miterLimit 才生效。lineWidth 和 strokeStyle 我们就不展开说明,本小节我们主要讲解后三个属性。
条形图严格的来说也是一种矩形图,只不过这个矩形非常长,故称为条形图。图标少的时候到还好,但是图标如果特别多的话,就不太适合放在一行里了,那么为什么还会有条形图这个概念呢?答案要从动画开始说起:众所周知,定义一个动画最简单的方式就是只定义开始时的状态和结束时的状态,中间的状态电脑会自动替你计算。所以如果是条形图的话只需要定义开始时在哪一个位置,再定义一个结束时在哪一个位置就够了,而且这种图一看就能知道大概是个什么样的动画。但是如果要是矩形图可就麻烦了:首先乍一看不太容易看出来大概是一个什么样的动画效果,不如上面那种条形图一目了然,其次定义动画时还要定义运行到百分之多少的时候转到下一行,然后再百分之多少再转,还要计算出下一行距离上一行的位置。要是这种规则的倒是还好,最起码有规律可循,但是就怕那种复杂雪碧图:可以看到人物与人物之间的间隔并不完全一样,时大时小,这时候就需要你去一点点的测量,然后调试,费了半天功夫才能出来一个相对令人满意的效果。当然条形图也怕这种间隔不规律的动画帧,但是相对于矩形图来说无论是测量还是调试都要更加方便一些。
我们先看一个案例:1425运行结果:根据我们两边的参考线,我们可以很清楚地看到 lineCap 的三个值的区别。butt:线段的端点是垂直于线段边缘的平直边缘。round:线段的端点是在线段边缘处多了一个以线宽为直径的半圆。square:线段的端点是在线段边缘处多了一个以线宽为长、以一半线宽为宽的矩形。在使用 lineCap 属性的时候,我们需要知道一点,lineCap 只作用于同一条路径的两端,比如一个折线,也只有在折线的两端才会生效。内容回顾还记得我们在学习【用线条绘制图形】那一小节中,列举了一个在firefox浏览器下终点和起点没有闭合的案例吗?本小节我们再用那个案例来说明一下 lineCap 属性的应用。案例:1426firefox运行结果:我们可以看到左上角的缺口已经不存在了。
这也是最常见的一种形式,顾名思义,整张大图是一个长方或正方形的:这种图片的好处就是一目了然,一打开便可以看到排列整齐的图标,然后找到你想要的图标来进行定位:但是这种矩形图在做动画的时候却不太方便,所以又引申出了另一种图形:条形图。
使用 rect 表示矩形,例如:1087包含6个属性x 用于表示矩形左上角坐标 x 值;y 用于表示矩形左上角坐标 y 值;width 表示矩形宽度;height 表示矩形高度;rx 用于实现圆角效果的圆角 x 轴半径;ry 用于实现圆角效果的圆角 y 轴半径。
条形图是用横向的柱子来展现数据,一般用于纵向的数据对比,其实就是 x 轴、y 轴对调的柱状图。例如上例中,对调 xAxis、yAxis 的配置值就可以实现条形图,示例:1355示例效果:
在幻灯片中添加自选图形,对应代码中访问,如下所示:...省略部分代码#添加自选图形shape=slide.shapes.add_shape(MSO_SHAPE.HEXAGON,Inches(2),Inches(2),Inches(5),Inches(3))#填充、边框fill=shape.fillfill.solid()fill.fore_color.rgb=RGBColor(255,0,0)line=shape.lineline.color.rgb=RGBColor(55,3,5)line.width=Pt(2)prs.save('test.pptx')代码解释:add_shape() 方法为添加自选图形,第一个参数为自选图形类型,这是一个枚举值,HEXAGON 为设置六边形(更多图形类型点击这里)后续参数依次对应 left,top,width,height。自选图形创建完成后,shape.fill 属性返回一个 FillFormat 对象,该对象包含指定图形的填充格式属性。首先通过solid()方法设置线填充,填充颜色使用RGB进行指定,通过 shape.line属性设置边线样式,包括边框颜色和宽度。执行完成后,test.pptx 演示文稿如下图所示。
如果我们要绘制多条折线,应该怎么做呢?这一小节我们就来画三条折线,为了区分三条折线,我们会用上一节学习的 strokeStyle 属性来给线段设定不同的颜色,本节还将学习一个新的属性 lineWidth ,该属性作用为设置线段宽度。首先,我们分别绘制三条折线。先看一个整体案例:1412运行结果:我们将上面的例子拆分讲解:获取 canvas 的渲染上下文。const canvas = document.getElementById('imooc');const ctx = canvas.getContext('2d');绘制第一条折线,折线的颜色为红色 (red),折线的宽度为 4px。ctx.moveTo(10,10);ctx.lineTo(100,50);ctx.lineTo(200,10);ctx.strokeStyle="red";ctx.lineWidth=4; //设置线段宽度为4pxctx.stroke();绘制第二条折线,折线的颜色为绿色 (green),折线的宽度为 5px。ctx.moveTo(10,30);ctx.lineTo(100,70);ctx.lineTo(200,30);ctx.strokeStyle="green"ctx.lineWidth=5; //设置线段宽度为5pxctx.stroke();绘制第三条折线,折线的颜色为蓝色 (blue),折线的宽度为 6px。ctx.moveTo(10,50);ctx.lineTo(100,100);ctx.lineTo(200,50);ctx.strokeStyle="blue"ctx.lineWidth=6; //设置线段宽度为6pxctx.stroke();通过运行上面的案例,我们有没有发现一个问题?那就是绘制出来的线段都是一个颜色,而且线段的宽度都是一样的,都是最后设置的蓝色和 6px 宽,这个是什么原因呢?这里我们需要明白一个原理就是:canvas 是基于状态的绘制。什么是“canvas 是基于状态的绘制?”我们用上面的案例来说明,上面案例中,每次使用 stroke() 时,都会把之前已经绘制的内容重新绘制一遍,例如开始 stroke() 第二条折线的时候,canvas 会把第一条折线重新再绘制一遍,开始 stroke() 第三条折线的时候,会把第一条折线和第二条折线再重新绘制一遍,之前绘制的折线不是消失了,而是被遮挡了。因为 canvas 是基于状态的绘制,所以我们这里看到了三条一样的折线,那么我们想要绘制不一样的折线应该怎么做呢?这里就需要用到新的方法 beginPath(),从字面意思我们可以知道,它的作用是重新开始一个路径。下面我们来看一个案例:1413运行结果:我们将上面的例子拆分讲解:获取 canvas 的渲染上下文。const canvas = document.getElementById('imooc');const ctx = canvas.getContext('2d');绘制第一条折线,折线的颜色为红色 (red),折线的宽度为 4px,第一个 beginPath 是可以省略的,因为 canvas 默认开始就是一个新的路径。ctx.beginPath() //开始一个新路径(第一个可以省略)ctx.moveTo(10,10);ctx.lineTo(100,50);ctx.lineTo(200,10);ctx.strokeStyle="red";ctx.lineWidth=4;ctx.stroke();绘制第二条折线,开始之前增加了 beginPath 方法,折线的颜色为绿色 (green),折线的宽度为 5px。ctx.beginPath()ctx.moveTo(10,30);ctx.lineTo(100,70);ctx.lineTo(200,30);ctx.strokeStyle="green"ctx.lineWidth=5; ctx.stroke();绘制第三条折线,开始之前增加了 beginPath 方法,折线的颜色为蓝色 (blue),折线的宽度为 6px。ctx.beginPath()ctx.moveTo(10,50);ctx.lineTo(100,100);ctx.lineTo(200,50);ctx.strokeStyle="blue"ctx.lineWidth=6;ctx.stroke();到这里我们就完成了多条折线的绘制。
二次贝塞尔曲线是一种二次曲线,它只能向一个方向弯曲,由三个点来定义:两个锚点及一个控制点,控制点用来控制曲线的形状。我们先看一下二次贝塞尔曲线的绘制过程的:二次贝塞尔曲线也可以用三个特征切线定义,曲线的第一部分与上下文点和控制点形成的虚线相切,曲线的顶部与 midpoint 1 和 midpoint 2 形成的虚线相切,曲线的最后部分与控制点和终点形成的虚线相切。如图所示:在 canvas 中,绘制二次贝塞尔曲线和我们前面学过的 lineTo 方法类似,都需要在当前上下文中存在一个已有路径的终点作为贝塞尔曲线的起点,既然起点是已知的,那么只需知道控制点和终点,就能唯一确定一条二次贝塞尔曲线。具体绘制方法为:ctx.quadraticCurveTo(cpx,cpy,x,y);先看整体案例:1437运行结果:这样我们就绘制了一条二次贝塞尔曲线。
我们先看一个案例,结合案例讲解能更好地理解线性渐变。1422运行结果:我们将上面的例子拆分讲解:获取canvas的渲染上下文。const canvas = document.getElementById('imooc');const ctx = canvas.getContext('2d');绘制一个矩形路径,左上角坐标是 (10, 10),长度为 200px,宽度是 100px。ctx.rect(10,10, 200,100)创建一条渐变线,起点是 (0, 0),终点是 (200, 0)。这里我们需要注意,这里的起点和终点是相对于整个画布坐标来讲的,和创建的路径是没有关联的。如果渐变线的范围超出了绘制的路径,则路径外的渐变颜色是不会显示的,如果渐变线的范围不足以填满绘制的路径,则会用两端的颜色填满路径。 let lg = ctx.createLinearGradient(0,0, 200,0)设定渐变的关键点。这里我们设定了三个关键点,这三个点设定了在这个位置的时候应该显示的颜色。这里的0,0.5,1都是指渐变线的起点到终点的比例。 lg.addColorStop(0, "#f00") lg.addColorStop(0.5, "#fff") lg.addColorStop(1, "#000")将创建的渐变样式赋值给填充属性。ctx.fillStyle = lg填充路径。ctx.fill() 到这里我们就给创建的路径填充了一个渐变色。用渐变色描边和填充操作一样,也是按上面讲的三个步骤操作,我们看一个案例。1423运行结果:我们可以看到描边的样式也是变成了渐变色。
arcTo() 是利用两条相交切线来确定圆弧的位置,开始前我们先要搞懂切线的几个知识点。如何确定切线?我们都知道两点确定一条直线,这里两条直线相交处有一个交点,交点是两条线共用的一个点,所以我们只需要三个点就能确定两条切线。根据切线如何确定圆心?切线的性质有:(1)切线和圆只有一个公共点。(2)切线和圆心的距离等于圆的半径。(3)切线垂直于经过切点的半径。(4)经过圆心垂直于切线的直线必过切点。(5)经过切点垂直于切线的直线必过圆心。我们根据切线的垂直线必过圆心,即可确定圆心。我们来看一张图片:上图中,只要我们确定了 PA、PB 这两条切线和圆的半径 OA,即可确定 AB 这条弧线。上图中,我们沿着 OP 延长线移动 O 点的位置,即可得到半径不同的圆,也就得到了不同的 AB 弧线。到这里我们明白了:有三个点就可以确定两条切线。有圆的半径就可以确定切线间的一条弧线。arcTo 就是利用上面的原理来绘制弧线的。arcTo 方法有5个参数,前两个参数表示的是上图中 P 点的坐标,也就是切线的交点,第3个和第4个参数表示 PB 切线上的任意一个坐标点,第5个参数表示的是上图中 OA 的长度,也就是绘制圆的半径。特别注意:第3、4个参数表示的点不是切点!第3、4个参数表示的点不是切点!第3、4个参数表示的点不是切点!arcTo 方法的参数中只有两个点和一个半径,我们前面讲到要绘制弧线,必须是三个点,那第一个点哪儿去了呢?其实第一个点就是当前画布中笔触所在的位置,也就是当前画布中已经绘制的路径的终点。先看整体案例:1433运行结果:我们对上面的绘制弧线代码做拆分讲解:开始一个新路径。ctx.beginPath();确定第一个坐标点 A 点,A 点是当前已有路径的终点。ctx.moveTo(40,40);我换一个写法:ctx.moveTo(40,0);ctx.lineTo(80,40);这时候 A 点的位置就变成了 (80, 40)。 根据切线交点、第二条切线上的某个点和半径开始绘制弧线。ctx.arcTo(260,40, 260,200, 60); //调用了绘制圆的函数这里需要注意三点:(1) A 点和 PA 切线的切点会被自动连接起来,但是 PB 切线上的切点和 B点不会自动连接起来。 (2) A 点肯定在路径上,B 点不一定在路径上。(3) 切点由 canvas 自动计算。设置绘制样式以及开始描边。ctx.strokeStyle = "#456795";ctx.lineWidth = 4;ctx.stroke();我们从案例中可以看到,绘制一个圆形路径只需要调用一个函数即可,arc 方法和我们之前学过的 rect 绘制矩形的方法类似,也是绘制了一个路径,我们后续对路径的描边或者填充依然是需要调用 stroke 或者 fill 方法的。
假如雪碧图上面所有的图标都一样大的话,建议做成条形图:因为条形图可以有个简便的方法:background-size: cover;cover的意思就是用宽高最小的那部分(上图就是高比较小)恰好能填充满整个背景区域。894运行结果:可以看到结果几乎是一样的,但是这种方式就不用我们一点点的去调尺寸,看究竟是哪个尺寸最合适。而且在调试位置的时候我们只需要关心一个方向的位置就可以了,方便了许多。
这个小节开始我们开始学习 ECharts 中的各个图形,这些图形都有着自己独特的风格,有着自己更适合的场景,在合理的场景下选择更为合适的图才能让我们的数据更好展示与分析。本节我们就先讲折线图这个稍微简单的图形。折线图用于显示数据在一个连续的时间间隔或者时间跨度上的变化,它的特点是反映事物随另一维度数值变化所产生趋势。
造成进程或者线程死锁有四个必要条件:(1)互斥条件:进程(线程)对于分配的资源有排他性,排他性是指一个资源在同一段时间内只能被一个进程(线程)占用。(2)请求和保持条件:进程(线程)因为请求资源导致阻塞时,对于已经获得资源不会主动释放。通俗来说就是已有的资源不会放弃,没有的资源会持续请求。(3)不可剥夺条件:进程(线程)在获得的资源没有使用完成之前,资源不能被剥夺,只能等进程(线程)主动释放。(4)循环等待条件:所有等待的进程(线程)在发生死锁时,都会形成一个死循环环路,这也是造成死锁的直接原因。
想要知道斜接长度限制就得先弄懂什么是斜接长度?斜接长度指的是在两条线交汇处内角和外角之间的距离。我们来看一张图,下图中的三个案例中两条竖线中间的距离表示的就是斜接长度。我们明白了什么是斜接长度,接下来就开始了解什么是斜接长度限制 miterLimit 了。我们借用预兆ZeD的一张图来看一下:miterLimit = 斜接长度 / 线条宽度(lineWidth) = 1 / sin ( min θ / 2 )。miterLimit 的默认值是10.0,所有的夹角小于 11 度的拐角都会超出这个限制。如果斜接长度超过 miterLimit 的值,拐角就会以 lineJoin 的 “bevel” 值来显示。miterLimit 最小值为1,如果设定小于1的值则会被忽略。我们看一个案例:1428运行结果:miterLimit 的主要目的是防止夹角过小导致绘图的尖角过长的问题。
大家可能觉得矩形不就是长方形嘛,那正方形呢?矩形是至少有三个内角都是直角的四边形。矩形是一种特殊的平行四边形,正方形是特殊的矩形。矩形也叫长方形。这是矩形的定义,也就是说四个角都是直角的四边形就可以叫做矩形,矩形也包括了正方形。
看过股市的朋友们对 K线图 这个图形肯定都不陌生。K线又称“阴阳烛”、“蜡烛线”,是反映价格走势的一种图线,其特色在于一个线段内记录了多项讯息,相当易读易懂且实用有效,广泛用于股票、期货、贵金属、数字货币等行情的技术分析,称为K线分析。
使用 createLinearGradient 方法可以绘制线性的渐变,适用于矩形、圆形、线条、文本等。1247绘制渐变对象,必须使用两种或两种以上的颜色。停止颜色,使用 addColorStop 方法指定颜色停止,参数为 0 - 1
由于 k 线图的数据项只能通过这种 4 位数组的格式定义,这会导致 k 线图与折线图、柱状图、散点图等其他直角坐标图表有些许不同:x、y 轴中必须有一条是类目轴,根据用户习惯,通常会选择 x 轴做类目轴;k 线图无法通过series.data 推断类目属性,所以类目轴必须通过 axis.data 项显式声明类目数据;k 线图的 series.data 与类目轴的 axis.data 根据数据出现的位置关联。当序列上只有一个 k 线图时,问题不大,但若要在同一坐标系上渲染多个 k 线图时,则需要对数据做一些额外的处理。比如,假设要展示下述四种蔬果的价格变化:[ { sku: '小台农芒果', data: [ ['2020-3-4', 9, 7, 14, 1], ['2020-3-6', 7, 3, 12, 1], ['2020-3-9', 3, 2, 8, 1], ['2020-3-12', 2, 1, 5, 1], ['2020-3-14', 1, 4, 6, 1], ['2020-3-16', 4, 7, 3, 1], ['2020-3-19', 7, 10, 3, 1], ['2020-3-21', 10, 12, 6, 1], ['2020-3-23', 12, 15, 5, 1], ['2020-3-25', 15, 13, 8, 1], ], }, { sku: '阿克苏苹果', data: [ ['2020-3-3', 6, 7, 13, 4], ['2020-3-6', 7, 5, 8, 3], ['2020-3-8', 5, 8, 11, 3], ['2020-3-9', 8, 11, 15, 2], ['2020-3-10', 11, 9, 13, 5], ['2020-3-11', 9, 12, 20, 2], ['2020-3-12', 12, 9, 16, 6], ['2020-3-15', 9, 11, 13, 6], ['2020-3-17', 11, 14, 19, 8], ['2020-3-19', 14, 17, 21, 11], ], }, { sku: '海南西州蜜瓜', data: [ ['2020-3-1', 16, 17, 23, 11], ['2020-3-2', 17, 15, 24, 10], ['2020-3-4', 15, 12, 20, 6], ['2020-3-7', 12, 9, 16, 3], ['2020-3-9', 9, 6, 12, 1], ['2020-3-12', 6, 3, 8, 1], ['2020-3-14', 3, 0, 5, 1], ['2020-3-17', 0, 2, 6, 1], ['2020-3-19', 2, 5, 8, 1], ['2020-3-21', 5, 8, 11, 1], ], }, { sku: '泰国椰青', data: [ ['2020-3-2', 18, 21, 22, 14], ['2020-3-3', 21, 19, 28, 13], ['2020-3-4', 19, 22, 27, 17], ['2020-3-6', 22, 21, 27, 17], ['2020-3-7', 21, 22, 24, 13], ['2020-3-9', 22, 21, 26, 16], ['2020-3-11', 21, 19, 23, 12], ['2020-3-13', 19, 21, 25, 15], ['2020-3-16', 21, 22, 23, 15], ['2020-3-18', 22, 20, 29, 19], ], },];注意,四个系列的统计时间不同,由于 k 线图无法自动推算类目轴的类别数据,所以第一步需要收集所有类目值:function retriveDates(series) { const categories = []; const len = series.length; for (let i = 0; i < len; i++) { // 找出尚未收集的时间值 const dates = series[i].data .map(([date]) => date) .filter((date) => categories.findIndex((cat) => cat === date) < 0); // 批量插入 categories.splice(categories.length, 0, ...dates); } return categories;}第二步,需要根据应用场景对收集到的类目值进行排序,本例中为简便起见,将借助 moment 库进行时间排序:function sort(categories) { const format = 'YYYY-MM-DD'; return categories.sort((d1, d2) => moment(d1, format) - moment(d2, format));}第三步,有了类目数据之后,还需要整理系列数据的顺序,使得数据与其对应的类目能够一一对应:function reschedule(series, categories) { return series.map(({ sku, data }) => { return { name: sku, data: categories.map((cat) => { const index = data.findIndex((c) => c === cat); return index >= 0 ? data[index].slice(1) : null; }), }; });}经过上述步骤就可以得到所有类目值及调整后的系列数据,完整代码:1375示例效果:
极坐标支持柱形图效果,配置上与折线图相似,多数情况下只需修改折线图的 type 为 bar 即可,示例:1314示例效果:
通过函数 fillRectangle 可以创建一个矩形,使用 fillStyle 属性为矩形填充颜色。1049
我们来展示与一下吕形布局的效果以及它的使用场景。吕形布局抽象起来是酱婶儿的:一般会把跳转页面、搜索、下载或者历史记录等功能放在上面固定在屏幕上的矩形内,可以简单的把它理解为标题或功能区:上面的矩形宽度充满屏幕,并提供一系列的按钮,每当点击时上方矩形按钮时下方矩形就会跳转到另一个页面,通常上方的一排按钮会有其中一个高亮显示,以告知用户当前所在的页面是哪一个。
Markdown 的目标是整个文档的风格统一,但是既然依托于 html 语法,那我们就依然能通过修改 CSS 的方式定制分割线的样式。实例 2:修改分割线的粗细### 分割线的尺寸#### 3px 宽线条___#### 5px 宽线条___#### 10px 宽线条___<style>hr:nth-of-type(1) { border-width: 3px 0 0 0 !important;}hr:nth-of-type(2) { border-width: 5px 0 0 0 !important;}hr:nth-of-type(3) { border-width: 10px 0 0 0 !important;}</style>渲染结果如下:实例 3:修改分割线的颜色:### 分割线的颜色#### 红色分割线___#### 蓝色分割线___#### 半透明的黑色分割线___#### 渐变色分割线___<style>hr:nth-of-type(1) { border-color: red !important;}hr:nth-of-type(2) { border-color: #00F !important;}hr:nth-of-type(3) { border-color: #0005 !important;}hr:nth-of-type(4) { border-image: linear-gradient(to right, #F00, #0F0 20%, #00F 80%, #000) 1 !important;}</style>渲染结果如下:实例 4:修改分割线的类型:### 分割线的类型#### 实线分隔线___#### 虚线分割线___#### 点状分割线___#### 双线分割线___#### 凹槽分割线___#### Inset分割线___#### Outset分割线___<style>hr { border-style: none !important; border-top-width: 5px !important;}hr:nth-of-type(1) { border-top-style: solid !important;}hr:nth-of-type(2) { border-top-style: dashed !important;}hr:nth-of-type(3) { border-top-style: dotted !important;}hr:nth-of-type(4) { border-top-style: double !important;}hr:nth-of-type(5) { border-top-style: groove !important;}hr:nth-of-type(6) { border-top-style: ridge !important;}hr:nth-of-type(7) { border-top-style: inset !important;}hr:nth-of-type(8) { border-top-style: outset !important;}</style>渲染结果如下:
我们先用上一小节学习的 rect 方法绘制一个矩形路径然后填充。先看整体案例:1419运行结果:我们从案例中可以看到,调用 fill 函数,会使用黑色把整个矩形框填满,这样的效果就是填充。当然,我们也可以利用 fillStyle 属性设定填充颜色。
canvas API 还提供了一个直接绘制矩形的方法 strokeRect(),这个方法调用和 rect 方法一样,也是接收4个参数。特别说明:利用 strokeRect 方法绘制的矩形独立于其他路径,后续对路径的操作不会影响到 strokeRect 绘制的矩形。先看整体案例:1418运行结果:我们从案例中可以看到,绘制一个矩形路径只需要调用一个函数即可,和 rect 方法比较,这里我们没有调用 stroke 方法了。
互斥条件:进程要求对所分配的资源进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待;不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放,如 yield 释放 CPU 执行权);请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放;循环等待条件:指在发生死锁时,必然存在一个线程请求资源的环形链,即线程集合 {T0,T1,T2,…Tn}中的 T0 正在等待一个 T1 占用的资源,T1 正在等待 T2 占用的资源,以此类推,Tn 正在等待己被 T0 占用的资源。如下图所示:
上一节我们学习到了折线图,相信大家对折线图的作用和使用场景都有所了解,这一节我们继续来看一下跟折线图相似的图形——柱状图。柱状图又称条形统计图,与折线图类似,用于表示事物多维属性之间的变化趋势。
我们利用 closePath 再绘制一个三角形,先看整体案例。1416运行结果:可以看到左上角闭合地非常完美。
时序图由以矩形代表参与者,参与者下方代表生存期间的长实线,连接线之间代表消息的箭头和控制焦点组成。时序图的内容也需要书写在「mermaid」类型代码块之间,如下:```mermaid```绘制时序图,必须包含时序图类型声明、对象及消息三个部分。实例 1:基本的时序图。```mermaidsequenceDiagram 李雷->>韩梅梅: Hi LiLei, How do you do? 韩梅梅-->>李雷: How do you do!```其渲染效果如下:时序图中的对象可以通过别名形式简化书写。实例 2:以别名形式定义对象。```mermaidsequenceDiagram participant l as 李雷 participant h as 韩梅梅 l->>h: Hello Hanmeimei, how are you? h->>l: Hello Lilei, I am fine, thank you, and you? l-->h: I am fine, thank you.```其渲染效果如下:时序图中的消息是对参与者之间通信的时机与内容的描述,其声明方式如:[发起者][连线类型][接收者]:消息内容。其中连接类型有如下几种形式:类型描述 -> 没有箭头的实线–> 没有箭头的虚线 ->> 有箭头的实线–>> 有箭头的虚线 -x 有交叉箭头的实线–x 有交叉箭头的虚线聚焦代表一条消息在其对象的生命周期中的处理活动。实例 3:在时序图中增加聚焦。```mermaidsequenceDiagram 李雷->>韩梅梅: Hello, what's your name? activate 韩梅梅 韩梅梅-->>李雷: Hello, my name is Hanmeimei! deactivate 韩梅梅```渲染效果如下:聚焦可以使用 + / - 符号简化书写。实例 4:聚焦的简写。sequenceDiagram 李雷->>+韩梅梅: Hello, what's your name? 韩梅梅-->>-李雷: Hello, my name is Hanmeimei!当我们无法用以上图形信息充分表达思路时,可以增加备注信息。备注需要通过单独一行的声明实现。实例 5:在参与者的生命线右侧增加备注。```mermaidsequenceDiagram 李雷->>韩梅梅: Hi Hanmeimei, How do you do? Note right of 韩梅梅: Lesson 1 韩梅梅-->>李雷: How do you do!```渲染效果如下:实例 6:在参与者的生命线之上增加备注。```mermaidsequenceDiagram 李雷->>韩梅梅: Hi Hanmeimei, How do you do? Note over 李雷,韩梅梅: Lesson 1 韩梅梅-->>李雷: How do you do!```渲染效果如下: