前端 | (十四)canvas基本用法 | 尚硅谷前端HTML5教程(html5入门经典)

文章目录

  • 📚canvas基本用法
    • 🐇什么是canvas(画布)
    • 🐇替换内容
    • 🐇canvas标签的两个属性
    • 🐇渲染上下文
  • 📚绘制矩形
    • 🐇绘制矩形
    • 🐇strokeRect时,边框像素渲染问题
    • 🐇添加样式和颜色
    • 🐇lineJoin
  • 📚绘制路径
    • 🐇canvas绘制路径
    • 🐇lineCap
    • 🐇save
    • 🐇restore
    • 🔥电子签名实例
  • 📚绘制曲线
    • 🐇canvas绘制圆形
    • 🐇arcTo
    • 🐇二次贝塞尔
    • 🐇三次贝塞尔
  • 📚canvas中的变换
    • 🐇translate(x, y)
    • 🐇rotate(angle)
    • 🐇scale(x, y)
    • 🔥放缩变换实例
    • 🔥画一个电子钟表
  • 📚图片
    • 🐇插入图片(需要image对象)
    • 🐇设置背景(需要image对象)
    • 🐇渐变
    • 🔥图片变动画
  • 📚文本
    • 🐇绘制文本
    • 🐇文本样式
    • 🐇阴影(文本阴影&盒模型阴影)
  • 📚像素操作
    • 🐇得到像素数据
    • 🔥马赛克实例
  • 📚合成
    • 🐇全局透明度的设置
    • 🐇合成
    • 🔥刮刮卡实例

  • 前端 | ( 十)HTML5简介及相关新增属性
  • 尚硅谷前端HTML5教程(html5入门经典)

📚canvas基本用法

🐇什么是canvas(画布)

  • <canvas> 是 HTML5 新增的元素,可用于通过使用JavaScript中的脚本来绘制图形。例如,它可以用于绘制图形,创建动画。<canvas> 最早由Apple引入WebKit。
  • 我们可以使用<canvas>标签来定义一个canvas元素
  • 使用<canvas>标签时,建议要成对出现,不要使用闭合的形式。
  • canvas元素默认具有高宽:width: 300pxheight:150px

🐇替换内容

  • <canvas>很容易定义一些替代内容。由于某些较老的浏览器(尤其是IE9之前的IE浏览器)不支持HTML元素"canvas",但在这些浏览器上你应该要给用户展示些替代内容。
  • 我们只需要在<canvas>标签中提供替换内容就可以。
  • 支持<canvas>的浏览器将会忽略在容器中包含的内容,并且只是正常渲染canvas。不支持<canvas>的浏览器会显示代替内容。
    <!-- 如果浏览器不支持​<canvas>元素,那么它会显示"您的浏览器不支持画布元素 请您换成萌萌的谷歌"。 -->
    <canvas id="test" width="300" height="300"><span>您的浏览器不支持画布元素 请您换成可爱的谷歌</span>
    </canvas>
    

🐇canvas标签的两个属性

  • <canvas> 看起来和 元素很相像,唯一的不同就是它并没有 src 和 alt 属性。
  • 实际上,<canvas> 标签只有两个属性—— width和height。这些都是可选的。
  • 当没有设置宽度和高度的时候,canvas会初始化宽度为300像素和高度为150像素。
  • 画布的高宽
    • html属性设置width height时只影响画布本身不影画布内容。
    • css属性设置width height时不但会影响画布本身的高宽,还会使画布中的内容等比例缩放(缩放参照于画布默认的尺寸)。

🐇渲染上下文

  • <canvas> 元素只是创造了一个固定大小的画布,要想在它上面去绘制内容,我们需要找到它的渲染上下文。
  • <canvas> 元素有一个叫做 getContext() 的方法,这个方法是用来获得渲染上下文和它的绘画功能。getContext()只有一个参数:上下文的格式
  • 获取方式
    var canvas = document.getElementById('box');
    var ctx = canvas.getContext('2d');
    
  • 检查支持性
    var canvas = document.getElementById('tutorial');
    if (canvas.getContext){var ctx = canvas.getContext('2d');
    } 
    

📚绘制矩形

🐇绘制矩形

  • canvas提供了三种方法绘制矩形:
    • 绘制一个填充的矩形(填充色默认为黑色):fillRect(x, y, width, height)
    • 绘制一个矩形的边框(默认边框为:一像素实心黑色):strokeRect(x, y, width, height)
    • 清除指定矩形区域,让清除部分完全透明:clearRect(x, y, width, height)
  • x与y指定了在canvas画布上所绘制的矩形的左上角(相对于原点)的坐标。
  • width和height设置矩形的尺寸(存在边框的话,边框会在width上占据一个边框的宽度,height同理)。
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title></title><style type="text/css">*{margin: 0;padding: 0;}html,body{height: 100%;overflow: hidden;}body{background: pink;}#test{background: gray;position: absolute;left: 0;top: 0;right: 0;bottom: 0;margin: auto;}</style></head><body><canvas id="test" width="300" height="300"><span>您的浏览器不支持画布元素 请您换成可爱的谷歌</span></canvas></body><script type="text/javascript">window.onload=function(){//拿到画布var canvas = document.querySelector("#test");if(canvas.getContext){var ctx = canvas.getContext("2d");//注意不加单位,填充的矩形ctx.fillRect(0,0,100,100)//带边框的矩形  ctx.strokeRect(100.5,100.5,100,100)// 清除指定矩形区域,让清除部分完全透明// ctx.clearRect(0,0,100,100)}}</script>
</html>

在这里插入图片描述

🐇strokeRect时,边框像素渲染问题

  • 按理渲染出的边框应该是1px的,canvas在渲染矩形边框时,边框宽度是平均分在偏移位置的两侧。
  • context.strokeRect(10,10,50,50):边框会渲染在10.5 和 9.5之间,浏览器是不会让一个像素只用自己的一半的,相当于边框会渲染在9到11之间。
  • context.strokeRect(10.5,10.5,50,50):边框会渲染在10到11之间。

🐇添加样式和颜色

  • fillStyle:设置图形的填充颜色。
  • strokeStyle:设置图形轮廓的颜色。
    • 默认情况下,线条和填充颜色都是黑色(CSS 颜色值 #000000)
  • lineWidth:这个属性设置当前绘线的粗细。属性值必须为正数。描述线段宽度的数字。 0、 负数、 Infinity 和 NaN 会被忽略。默认值是1.0。

🐇lineJoin

  • 设定线条与线条间接合处的样式(默认是 miter)

    • round : 圆角
    • bevel : 斜角
    • miter : 直角
    <!DOCTYPE html>
    <html><head><meta charset="UTF-8"><title></title><style type="text/css">*{margin: 0;padding: 0;}html,body{height: 100%;overflow: hidden;}body{background: pink;}#test{background: gray;position: absolute;left: 0;top: 0;right: 0;bottom: 0;margin: auto;}</style></head><body><canvas id="test" width="300" height="300"><span>您的浏览器不支持画布元素 请您换成可爱的谷歌</span></canvas></body><script type="text/javascript">window.onload=function(){//拿到画布var canvas = document.querySelector("#test");if(canvas.getContext){var ctx = canvas.getContext("2d");// 设置图形的填充颜色ctx.fillStyle="deeppink";// 设置图形轮廓的颜色ctx.strokeStyle="pink";// 当前绘线的粗细ctx.lineWidth=25;// 设定线条与线条间接合处的样式ctx.lineJoin="round";ctx.strokeRect(100,100,100,100)ctx.fillRect(0,0,100,100)//ctx.clearRect(100,100,100,100)}}</script>
    </html>
    

在这里插入图片描述


  • rect画矩形
    var canvas = document.querySelector("#test");
    if(canvas.getContext){var ctx = canvas.getContext("2d");ctx.rect(50,50,100,100);ctx.fillStyle="skyblue";ctx.strokeStyle="black";ctx.fill();ctx.stroke();
    }
    

在这里插入图片描述

📚绘制路径

🐇canvas绘制路径

图形的基本元素是路径。路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。

  • 首先,创建路径起始点

    • beginPath():新建一条路径,生成之后,图形绘制命令被指向到路径上准备生成路径。
      • 本质上,路径是由很多子路径构成,这些子路径都是在一个列表中,所有的子路径(线、弧形、等等)构成图形。
      • 而每次这个方法调用之后,列表清空重置,然后我们就可以重新绘制新的图形。
  • 使用画图命令去画出路径

    • moveTo(x, y)
      • 将笔触移动到指定的坐标x以及y上
      • 当canvas初始化或者beginPath()调用后,你通常会使用moveTo()函数设置起点
    • lineTo(x, y)
      • 将笔触移动到指定的坐标x以及y上
      • 绘制一条从当前位置到指定x以及y位置的直线。
  • 之后你把路径封闭

    • closePath()
      • 闭合路径之后图形绘制命令又重新指向到上下文中。
      • 闭合路径closePath(),不是必需的。这个方法会通过绘制一条从当前点到开始点的直线来闭合图形。
      • 如果图形是已经闭合了的,即当前点为开始点,该函数什么也不做。当你调用fill()函数时,所有没有闭合的形状都会自动闭合,所以你不需要调用closePath()函数。但是调用stroke()时不会自动闭合。
      • stroke():通过线条来绘制图形轮廓。不会自动调用closePath()。
      • fill():通过填充路径的内容区域生成实心的图形。自动调用closePath()。
  • 一旦路径生成,你就能通过描边或填充路径区域来渲染图形。

    <!DOCTYPE html>
    <html><head><meta charset="UTF-8"><title></title><style type="text/css">*{margin: 0;padding: 0;}html,body{height: 100%;overflow: hidden;}body{background: pink;}#test{background: gray;position: absolute;left: 0;top: 0;right: 0;bottom: 0;margin: auto;}</style></head><body><canvas id="test" width="300" height="300"><span>您的浏览器不支持画布元素 请您换成可爱的谷歌</span></canvas></body><script type="text/javascript">window.onload=function(){var canvas = document.querySelector("#test");if(canvas.getContext){var ctx = canvas.getContext("2d");ctx.strokeStyle="deeppink";ctx.lineWidth=10;ctx.moveTo(100,100);ctx.lineTo(100,200);ctx.lineTo(200,200);ctx.closePath();ctx.stroke();//fill方法会自动合并路径ctx.fill();}}</script>
    </html>
    

在这里插入图片描述

🐇lineCap

  • lineCap 是 Canvas 2D API 指定如何绘制每一条线段末端的属性。有3个可能的值,分别是:
    • butt:线段末端以方形结束。
    • round:线段末端以圆形结束
    • square:线段末端以方形结束,但是增加了一个宽度和线段相同,高度是线段厚度一半的矩形区域
    • 默认值是 butt。
    var canvas = document.querySelector("#test");
    if(canvas.getContext){var ctx = canvas.getContext("2d");ctx.strokeStyle="pink";ctx.lineWidth=10;ctx.lineCap="round";	ctx.moveTo(100,100);ctx.lineTo(100,200);ctx.stroke();
    }
    

在这里插入图片描述

🐇save

  • save() 是 Canvas 2D API 通过将当前状态放入栈中,保存 canvas 全部状态的方法。保存到栈中的绘制状态有下面部分组成:
    • 当前的变换矩阵。
    • 当前的剪切区域。
    • 当前的虚线列表。
    • 以下属性当前的值: strokeStyle, fillStyle, lineWidth, lineCap, lineJoin…

🐇restore

  • restore() 是 Canvas 2D API 通过在绘图状态栈中弹出顶端的状态,将 canvas 恢复到最近的保存状态的方法。

  • 如果没有保存状态,此方法不做任何改变。

    var canvas = document.querySelector("#test");
    if(canvas.getContext){var ctx = canvas.getContext("2d");ctx.save();ctx.fillStyle="pink";ctx.save();// 没保存的不入栈ctx.fillStyle="deeppink";ctx.fillStyle="blue";ctx.save();ctx.fillStyle="red";ctx.save();ctx.fillStyle="green";ctx.save();ctx.beginPath();// 弹出,restore四个是pink,三个是blue,二个是red,一个是greenctx.restore();ctx.restore();ctx.restore();ctx.restore();ctx.fillRect(50,50,100,100);
    }
    

在这里插入图片描述


🔥电子签名实例

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title></title><style type="text/css">*{margin: 0;padding: 0;}body{background: #dbd0ff;}#test{position: absolute;left: 0;right: 0;top: 0;bottom: 0;margin: auto;background:white;}</style></head><body><canvas id="test" width="500" height="500"></canvas></body><script type="text/javascript">window.onload=function(){var canvas =document.getElementById("test");if(canvas.getContext){var ctx = canvas.getContext("2d");}// 在鼠标按下时触发事件canvas.onmousedown=function(ev){// 获取事件对象ev = ev || window.event;if(canvas.setCapture){// 设置鼠标捕获,确保鼠标事件在元素上持续触发canvas.setCapture();}ctx.beginPath();// ev.clientX表示鼠标相对于文档可视区域的水平坐标,canvas.offsetLeft表示画布相对于文档可视区域的水平偏移量ctx.moveTo(ev.clientX -canvas.offsetLeft,ev.clientY -canvas.offsetTop);document.onmousemove=function(ev){// 在整个文档区域内移动鼠标时触发事件// 保存画布当前的状态ctx.save();ctx.strokeStyle="black";ev = ev || event;// 创建从当前点到指定点的线段,即绘制直线ctx.lineTo(ev.clientX -canvas.offsetLeft,ev.clientY -canvas.offsetTop);// 绘制已定义的路径ctx.stroke();ctx.restore();}// 释放鼠标捕获document.onmouseup=function(){document.onmousemove=document.onmouseup=null;if(document.releaseCapture){document.releaseCapture();}}return false;}}</script>
</html>

在这里插入图片描述

📚绘制曲线

  • 角度与弧度的js表达式:radians=(Math.PI/180)*degrees

🐇canvas绘制圆形

  • arc(x, y, radius, startAngle, endAngle, anticlockwise)
    • 画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成。ture:逆时针,false:顺时针
    • x,y为绘制圆弧所在圆上的圆心坐标,radius为半径
    • startAngle以及endAngle参数用弧度定义了开始以及结束的弧度。这些都是以x轴为基准参数anticlockwise 为一个布尔值。为true时,是逆时针方向,否则顺时针方向。
    var canvas = document.querySelector("#test");
    if(canvas.getContext){var ctx = canvas.getContext("2d");ctx.beginPath();ctx.moveTo(100,100);ctx.arc(100,100,50,0,90*Math.PI/180,true);ctx.closePath();ctx.stroke();
    }
    

在这里插入图片描述

🐇arcTo

  • arcTo(x1, y1, x2, y2, radius),根据给定的控制点和半径画一段圆弧。

  • 相当于指定了起点和终点的切线(就是下边示例中,画出的直线就是在画弧线时的切线)

    var canvas = document.querySelector("#test");
    if(canvas.getContext){var ctx = canvas.getContext("2d");ctx.strokeStyle="purple";ctx.beginPath();ctx.moveTo(50,50);ctx.lineTo(300,0);ctx.lineTo(200,200);ctx.stroke();ctx.beginPath();ctx.moveTo(50,50)ctx.arcTo(300,0,200,200,50);ctx.stroke();
    }
    

在这里插入图片描述

🐇二次贝塞尔

  • quadraticCurveTo(cp1x, cp1y, x, y)
    • 绘制二次贝塞尔曲线,cp1x,cp1y为一个控制点,x,y为结束点。
    • 起始点为moveto时指定的点。
    var ctx = canvas.getContext("2d");
    ctx.strokeStyle="purple";
    ctx.beginPath();
    ctx.moveTo(50,50);
    ctx.lineTo(300,0);
    ctx.lineTo(200,200);
    ctx.stroke();ctx.beginPath();
    ctx.moveTo(50,50)
    ctx.quadraticCurveTo(300,0,200,200);
    ctx.stroke();
    

在这里插入图片描述

🐇三次贝塞尔

  • bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
    • 绘制三次贝塞尔曲线,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,x,y为结束点。
    • 起始点为moveto时指定的点。
    var canvas = document.querySelector("#test");
    if(canvas.getContext){var ctx = canvas.getContext("2d");ctx.beginPath();ctx.moveTo(50,50);ctx.lineTo(300,0);ctx.lineTo(0,300);ctx.lineTo(300,300);ctx.stroke();ctx.strokeStyle="purple";ctx.beginPath();ctx.moveTo(50,50)ctx.bezierCurveTo(300,0,0,300,300,300);ctx.stroke();
    }
    

在这里插入图片描述

📚canvas中的变换

🐇translate(x, y)

  • 我们先介绍 translate 方法,它用来移动 canvas的原点到一个不同的位置。
  • translate 方法接受两个参数。x 是左右偏移量,y 是上下偏移量,在 canvas中translate是累加的。
    var canvas = document.querySelector("#test");
    if(canvas.getContext){var ctx = canvas.getContext("2d");ctx.translate(100,100)ctx.beginPath();ctx.fillStyle="skyblue";ctx.fillRect(0,0,100,100);
    }
    

在这里插入图片描述

🐇rotate(angle)

  • 这个方法只接受一个参数:旋转的角度(angle),它是顺时针方向的,以弧度为单位的值。
  • 旋转的中心点始终是 canvas 的原点,如果要改变它,我们需要用到 translate 方法,在canvas中rotate是累加的。
    var canvas = document.querySelector("#test");
    if(canvas.getContext){var ctx = canvas.getContext("2d");ctx.translate(70,70)ctx.rotate(22.1*Math.PI/180)ctx.rotate(22.9*Math.PI/180)ctx.beginPath();ctx.fillStyle="skyblue";ctx.fillRect(50,50,100,100);
    }
    

在这里插入图片描述

🐇scale(x, y)

  • scale 方法接受两个参数。x,y 分别是横轴和纵轴的缩放因子,它们都必须是正值。

  • 值比 1.0 小表示缩小,比 1.0 大则表示放大,值为 1.0 时什么效果都没有。

  • 缩放一般我们用它来增减图形在 canvas 中的像素数目,对形状,位图进行缩小或者放大。

  • 在canvas中scale是累积的。

    var ctx = canvas.getContext("2d");
    ctx.fillStyle="skyblue";
    ctx.beginPath();
    ctx.fillRect(50,50,100,100);
    ctx.beginPath();
    ctx.fillStyle="green";
    ctx.scale(0.5,0.5)
    ctx.fillRect(50,50,100,100);
    ctx.beginPath();
    ctx.fillStyle="yellow";
    // 因为累加,相当于两倍
    ctx.scale(4,4)
    ctx.fillRect(50,50,100,100);
    

在这里插入图片描述

🔥放缩变换实例

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title></title><style type="text/css">*{margin: 0;padding: 0;}html,body{height: 100%;background-color: #dbdbff;overflow: hidden;}#test{background: rgba(143, 143, 253, 0.785);position: absolute;left: 0;top: 0;right: 0;bottom: 0;margin: auto;}</style></head><body><canvas id="test" width="300" height="300"><span>您的浏览器不支持画布元素 请您换成可爱的谷歌</span></canvas></body><script type="text/javascript">window.onload=function(){var angle =0;//角度var scale = 0;//比例var angleScale = 0;//状态var canvas = document.querySelector("#test");if(canvas.getContext){var ctx = canvas.getContext("2d");ctx.fillStyle="rgba(10, 10, 79, 0.785)";ctx.save();// 将坐标原点平移到150,150ctx.translate(150,150);ctx.beginPath();ctx.fillRect(-50,-50,100,100);ctx.restore();// 设定定时器函数,每隔10毫秒执行一次setInterval(function(){// 每次执行时,将旋转角度标记加1angle++;ctx.clearRect(0,0,canvas.width,canvas.height);ctx.save();ctx.translate(150,150);// 旋转ctx.rotate(angle*Math.PI/180);// 0~100之间缩放if(scale==100){angleScale=-1;}else if(scale==0){angleScale=1;}scale+=angleScale;// 缩放ctx.scale(scale/50,scale/50);ctx.beginPath();ctx.fillRect(-50,-50,100,100);ctx.restore();},10)}}</script>
</html>

在这里插入图片描述

🔥画一个电子钟表

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title></title><style type="text/css">*{margin: 0;padding: 0;}html,body{height: 100%;overflow: hidden;background: rgb(197, 197, 255);}#clock{background: rgb(167, 208, 255);position: absolute;left: 50%;top: 50%;transform: translate3d(-50%,-50%,0);}</style></head><body><canvas id="clock" width="400" height="400"></canvas></body><script type="text/javascript">window.onload=function(){var clock = document.querySelector("#clock"); if(clock.getContext){ var ctx = clock.getContext("2d"); // 定时器函数,每隔1秒执行一次setInterval(function(){ // 清除整个画布的内容ctx.clearRect(0,0,clock.width,clock.height); // 调用move函数move(); },1000);move(); // move函数,用于绘制时钟function move(){ ctx.save(); ctx.lineWidth = 8; ctx.strokeStyle = "black"; // 设置线条末端的形状为圆形ctx.lineCap = "round"; // 将坐标原点平移到(200, 200)的位置ctx.translate(200,200); // 对画布进行旋转操作,旋转的角度为-90°,即将角度转换为弧度单位,并逆时针旋转ctx.rotate(-90*Math.PI/180); ctx.beginPath(); // 外层空心圆盘ctx.save(); ctx.strokeStyle="#325FA2"; ctx.lineWidth = 14;ctx.beginPath();// 绘制一个从0°到360°的弧线,即画一个圆ctx.arc(0,0,140,0,360*Math.PI/180); ctx.stroke(); // 绘制已定义的路径ctx.restore(); // 恢复之前保存的画布状态// 时针刻度ctx.save(); // 循环绘制12个时针刻度for(var i=0;i<12;i++){ // 逆时针旋转30°,即将角度转换为弧度单位ctx.rotate(30*Math.PI/180); ctx.beginPath(); ctx.moveTo(100,0) ctx.lineTo(120,0); ctx.stroke(); }ctx.restore(); // 分针刻度ctx.save(); ctx.lineWidth=4; // 循环绘制60个分针刻度for(var i=0;i<60;i++){ // 逆时针旋转6°,即将角度转换为弧度单位ctx.rotate(6*Math.PI/180); // 如果不是整点刻度,则绘制if((i+1)%5!=0){ ctx.beginPath();ctx.moveTo(117,0) ctx.lineTo(120,0); ctx.stroke(); }}ctx.restore(); // 时针、分针、秒针、表座// 创建一个Date对象,获取当前时间var date = new Date(); // 获取当前的秒数var s = date.getSeconds(); // 获取当前的分钟数,并加上秒数的部分var m = date.getMinutes()+s/60; // 获取当前的小时数,并加上分钟数的部分var h = date.getHours()+m/60; // 如果小时数大于12,则减去12h = h>12?h-12:h; // 时针ctx.save() ctx.lineWidth=14; // 逆时针旋转小时数乘以30°,即将角度转换为弧度单位ctx.rotate(h*30*Math.PI/180) ctx.beginPath() ctx.moveTo(-20,0); ctx.lineTo(80,0);ctx.stroke(); ctx.restore() // 分针ctx.save() ctx.lineWidth=10; // 逆时针旋转分钟数乘以6°,即将角度转换为弧度单位ctx.rotate(m*6*Math.PI/180) ctx.beginPath() ctx.moveTo(-28,0); ctx.lineTo(112,0); ctx.stroke(); ctx.restore() // 秒针ctx.save() ctx.lineWidth=6; ctx.strokeStyle="#D40000"; ctx.fillStyle="#D40000"; // 逆时针旋转秒数乘以6°,即将角度转换为弧度单位ctx.rotate(s*6*Math.PI/180) ctx.beginPath(); ctx.moveTo(-30,0); ctx.lineTo(83,0); ctx.stroke(); // 表座ctx.beginPath(); ctx.arc(0,0,10,0,360*Math.PI/180); ctx.fill(); // 秒针头ctx.beginPath(); ctx.arc(96,0,10,0,360*Math.PI/180);ctx.stroke(); ctx.restore() ctx.restore(); }}
}</script>
</html>

在这里插入图片描述

📚图片

🐇插入图片(需要image对象)

  • canvas操作图片时,必须要等图片加载完才能操作
  • drawImage(image, x, y, width, height):其中 image 是 image 或者 canvas 对象,x 和 y 是其在目标 canvas 里的起始坐标。这个方法多了2个参数:width 和 height,这两个参数用来控制 当像canvas画入时应该缩放的大小
    var ctx = canvas.getContext("2d");
    var img = new Image();
    img.src="tg.png";
    img.onload=function(){draw();
    }
    function draw(){ctx.drawImage(img,0,0,img.width,img.height)
    }
    

🐇设置背景(需要image对象)

  • createPattern(image, repetition)

  • image:图像源

  • epetition:“repeat” 、“repeat-x” 、“repeat-y” 、“no-repeat”

  • 一般情况下,我们都会将createPattern返回的对象作为fillstyle的值

    var canvas = document.querySelector("#test");
    if(canvas.getContext){var ctx = canvas.getContext("2d");var img = new Image();img.src="tg.png";img.onload=function(){draw();}function draw(){var pattern = ctx.createPattern(img,"no-repeat")ctx.fillStyle=pattern;ctx.fillRect(0,0,300,300);}	}
    

🐇渐变

  • 线性渐变:createLinearGradient(x1, y1, x2, y2),表示渐变的起点 (x1,y1) 与终点 (x2,y2)
  • gradient.addColorStop(position, color)
    • gradient :createLinearGradient的返回值
    • addColorStop 方法接受 2 个参数,position 参数必须是一个 0.0 与 1.0 之间的数值,表示渐变中颜色所在的相对位置。例如,0.5 表示颜色会出现在正中间。
    • color 参数必须是一个有效的 CSS 颜色值(如 #FFF, rgba(0,0,0,1),等等)。
    var canvas = document.querySelector("#test");
    if(canvas.getContext){var ctx = canvas.getContext("2d");var gradient =  ctx.createLinearGradient(0, 0, 200, 200);gradient.addColorStop(0,"red");gradient.addColorStop(0.5,"yellow");gradient.addColorStop(0.7,"pink");gradient.addColorStop(1,"green");ctx.fillStyle=gradient;ctx.fillRect(0,0,300,300);
    }
    

在这里插入图片描述


  • 径向渐变:createRadialGradient(x1, y1, r1, x2, y2, r2)

    • 前三个参数则定义另一个以(x1,y1) 为原点,半径为 r1 的圆
    • 后三个参数则定义另一个以 (x2,y2) 为原点,半径为 r2 的圆
    var canvas = document.querySelector("#test");
    if(canvas.getContext){var ctx = canvas.getContext("2d");var gradient = ctx.createRadialGradient(150, 150, 50, 150, 150, 100)gradient.addColorStop(0,"red");gradient.addColorStop(0.5,"yellow");gradient.addColorStop(0.7,"pink");gradient.addColorStop(1,"green");ctx.fillStyle=gradient;ctx.fillRect(0,0,300,300);	
    }
    

在这里插入图片描述

🔥图片变动画

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title></title><style type="text/css">*{margin: 0;padding: 0;}html,body{height: 100%;overflow: hidden;}#test{background: white;position: absolute;left: 0;top: 0;right: 0;bottom: 0;margin: auto;}</style></head><body><canvas id="test" width="300" height="300"></canvas></body><script type="text/javascript">window.onload=function(){var canvas = document.querySelector("#test");canvas.width = document.documentElement.clientWidth;canvas.height = document.documentElement.clientHeight;if(canvas.getContext){var ctx = canvas.getContext("2d");// 用于标记当前显示的图片序号var flag = 0;// 用于控制图片在x轴的偏移量var value=0;setInterval(function(){ctx.clearRect(0,0,canvas.width,canvas.height)value+=10;flag++;if(flag==9){// 当图片序号达到9时,重新设置为1flag=1;}var img = new Image();img.src="img/q_r"+(flag)+".jpg";img.onload=function(){draw(this);}	},100)function draw(img){ctx.drawImage(img,value,0)}}}	</script>
</html>

在这里插入图片描述

📚文本

🐇绘制文本

  • canvas 提供了两种方法来渲染文本:

    • fillText(text, x, y):在指定的(x,y)位置填充指定的文本
    • strokeText(text, x, y):在指定的(x,y)位置绘制文本边框
    var canvas = document.querySelector("#test");
    if(canvas.getContext){var ctx = canvas.getContext("2d");ctx.fillStyle="skyblue"ctx.font="40px sans-serif"ctx.fillText("lalayouyi",70,150);ctx.strokeText("lalayouyi",70,150);
    }
    

在这里插入图片描述

🐇文本样式

  • font = value

    • 当前我们用来绘制文本的样式,这个字符串使用和 CSS font 属性相同的语法。
    • 默认的字体是 10px sans-serif。
    • font属性在指定时,必须要有。大小和字体 缺一不可
  • textAlign = value:文本对齐选项。可选的值包括: left, right center.

  • textBaseline = value:描述绘制文本时,当前文本基线的属性。

  • measureText() 方法返回一个 TextMetrics 对象,包含关于文本尺寸的信息(例如文本的宽度)

  • canvas中文本水平垂直居中

    var oC =document.getElementById('c1');
    var oGC = oC.getContext('2d');
    oGC.font = '60px impact';
    oGC.textAlign = 'left';
    oGC.textBaseline = 'middle'; 
    var w = oGC.measureText('lalayouyi').width;
    oGC.fillText('lalayouyi',(oC.width - w)/2 , (oC.height - 60)/2);
    

在这里插入图片描述

🐇阴影(文本阴影&盒模型阴影)

  • shadowOffsetX = float:shadowOffsetX 和 shadowOffsetY 用来设定阴影在 X 和 Y 轴的延伸距离,它们默认都为 0。
  • shadowOffsetY = float:shadowOffsetX 和 shadowOffsetY 用来设定阴影在 X 和 Y 轴的延伸距离,它们默认都为 0。
  • shadowBlur = float:shadowBlur 用于设定阴影的模糊程度,其数值并不跟像素数量挂钩,也不受变换矩阵的影响,默认为 0。
  • shadowColor = color(必需项):shadowColor 是标准的 CSS 颜色值,用于设定阴影颜色效果,默认是全透明的黑色。
    var oC =document.getElementById('c1');
    var oGC = oC.getContext('2d');
    oGC.font = '60px impact';
    oGC.textAlign = 'left';
    oGC.textBaseline = 'middle'; 
    var w = oGC.measureText('lalayouyi').width;
    //文本阴影&盒阴影
    oGC.shadowOffsetX = 20;
    oGC.shadowOffsetY = 20;
    oGC.shadowBlur = 30;
    oGC.shadowColor = "yellow";
    oGC.fillText('lalayouyi',(oC.width - w)/2 , (oC.height - 60)/2);
    

在这里插入图片描述

📚像素操作

到目前为止,我们尚未深入了解Canvas画布真实像素的原理,事实上,你可以直接通过ImageData对象操纵像素数据,直接读取或将数据数组写入该对象中。

🐇得到像素数据

  • getImageData():获得一个包含画布场景像素数据的ImageData对像,它代表了画布区域的对象数据
  • ctx.getImageData(sx, sy, sw, sh)
    • sx:将要被提取的图像数据矩形区域的左上角 x 坐标。
    • sy:将要被提取的图像数据矩形区域的左上角 y 坐标。
    • sw:将要被提取的图像数据矩形区域的宽度。
    • sh:将要被提取的图像数据矩形区域的高度。
  • ImageData对象中存储着canvas对象真实的像素数据,它包含以下几个只读属性:
    • width:图片宽度,单位是像素
    • height:图片高度,单位是像素
    • data:Uint8ClampedArray类型的一维数组

  • putImageData()方法去对场景进行像素数据的写入。
  • putImageData(myImageData, dx, dy):dx和dy参数表示你希望在场景内左上角绘制的像素数据所得到的设备坐标。
  • ctx.createImageData(width, height):创建一个ImageData对象,默认创建出来的是透明的。
    var ctx = canvas.getContext("2d");
    // 设置填充样式为"rgba(255, 192, 203,1)"
    ctx.fillStyle="rgba(255, 192, 203,1)";
    ctx.fillRect(0,0,100,100);
    // 获取矩形区域(0, 0, 100, 100)内的图像数据
    var imageData = ctx.getImageData(0,0,100,100);
    // var imageData = ctx.createImageData(100,100);
    // 透明度即为数组 ​imageData.data​中下标为4i+3的值(其中i表示像素点的索引)。
    // 将每个像素点的透明度设置为100,即不完全透明状态。
    for(var i=0;i<imageData.data.length;i++){imageData.data[4*i+3]=100;
    }
    // 重新绘制
    ctx.putImageData(imageData,0,0)
    

在这里插入图片描述

🔥马赛克实例

  • 流程

    • 创建一个Image对象,并将其src属性设置为图片路径。
    • 在图片加载完成后,设置canvas的宽度为图片宽度的两倍,高度为图片高度。调用draw函数绘制原始图片。
    • 在draw函数中
      • 首先使用ctx.drawImage方法绘制原始图片。
      • 使用ctx.getImageData方法获取原始图片的图像数据,并保存在oldImgdata变量中。
      • 使用ctx.createImageData方法创建新的图像数据,并保存在newImgdata变量中。
      • 定义size变量为5,表示每个小方块的大小为5像素。
      • 使用嵌套的循环遍历每个小方块内的像素点,随机获取小方块内某个像素点的颜色信息,并将该颜色信息应用于整个小方块内的像素。
      • 清除画布内容。
      • 使用ctx.putImageData方法将修改后的新图像数据绘制到画布上。
  • 通过获取和设置图像数据的像素颜色信息,将原始图片划分为小块,并在每个小块中随机选择一个像素点的颜色信息,并应用于整个小块内的所有像素点,从而实现像素化的效果。

    <!DOCTYPE html>
    <html><head><meta charset="UTF-8"><title></title><style type="text/css">*{margin: 0;padding: 0;}html,body{height: 100%;overflow: hidden;}#msk{position: absolute;left: 50%;top: 50%;transform: translate3d(-50%,-50%,0);}</style></head><body><canvas id="msk" ></canvas></body><script type="text/javascript">var oc = document.querySelector("#msk");if(oc.getContext){var ctx = oc.getContext("2d");var img = new Image();img.src="2.png";img.onload=function(){oc.width=img.width*2;oc.height=img.height;draw();}function draw(){ctx.drawImage(img,0,0);var oldImgdata = ctx.getImageData(0,0,img.width,img.height);var newImgdata = ctx.createImageData(img.width,img.height);// 定义每个小方块的大小为5像素var size = 5;for(var i=0;i<oldImgdata.width/size;i++){for(var j=0;j<oldImgdata.height/size;j++){// 获取当前小方块内某个像素的颜色信息var color = getPxInfo(oldImgdata,i*size+Math.floor(Math.random()*size),j*size+Math.floor(Math.random()*size));for(var a=0;a<size;a++){for(var b=0;b<size;b++){// 设置新图像数据中的像素颜色信息setPxInfo(newImgdata,i*size+a,j*size+b,color)}}}}ctx.clearRect(0,0,oc.width,oc.height);ctx.putImageData(newImgdata,0,0);}// 获取指定位置像素的颜色信息function getPxInfo(imgdata,x,y){var color = [];var data = imgdata.data;var w = imgdata.width;var h = imgdata.height;// 获取像素点的红色值color[0]=data[(y*w+x)*4];// 获取像素点的绿色值color[1]=data[(y*w+x)*4+1];// 获取像素点的蓝色值color[2]=data[(y*w+x)*4+2];// 获取像素点的透明度值color[3]=data[(y*w+x)*4+3];// 返回颜色信息数组return color;}// 设置指定位置像素的颜色信息function setPxInfo(imgdata,x,y,color){var data = imgdata.data;var w = imgdata.width;var h = imgdata.height;data[(y*w+x)*4]=color[0];data[(y*w+x)*4+1]=color[1];data[(y*w+x)*4+2]=color[2];data[(y*w+x)*4+3]=color[3];}}</script>
    </html>
    

在这里插入图片描述

📚合成

🐇全局透明度的设置

globalAlpha = value。这个属性影响到 canvas 里所有图形的透明度,有效的值范围是 0.0 (完全透明)到 1.0(完全不透明),默认是 1.0。

var ctx = canvas.getContext("2d");
ctx.fillStyle="red";
ctx.globalAlpha=.4;
ctx.fillRect(0,0,100,100);
ctx.fillRect(100,100,100,100);

在这里插入图片描述

🐇合成

globalCompositeOperation属性用于设置绘制图形时的组合操作方式。组合操作可以理解为将已有的图形和新绘制的图形进行合并或者覆盖的方式。该属性的值可以是以下几种:

  • "source-over"(默认值):新绘制的图形覆盖在已有图形之上。这是最常用的组合操作方式。

  • "source-in":新绘制的图形只显示于已有图形的重叠部分,其他部分不可见。

  • "source-out":新绘制的图形只显示于已有图形之外的部分,与已有图形重叠的部分不可见。

  • "source-atop":新绘制的图形显示于已有图形上方的重叠部分,与已有图形下方的部分不可见。

  • "destination-over":已有的图形覆盖在新绘制的图形之上。

  • "destination-in":已有的图形只显示于新绘制图形的重叠部分,其他部分不可见。

  • "destination-out":已有的图形只显示于新绘制图形之外的部分,与新绘制图形重叠的部分不可见。

  • "destination-atop":已有的图形显示于新绘制图形上方的重叠部分,与新绘制图形下方的部分不可见。

  • "lighter":将已有的图形和新绘制的图形颜色混合,得到更亮的颜色。

  • "copy":新绘制的图形完全替换已有的图形。

  • "xor":新绘制的图形与已有的图形进行异或运算,产生一种类似于橡皮擦的效果。

var ctx = canvas.getContext("2d");
ctx.fillStyle="pink";
ctx.fillRect(50,50,100,100);
// 控制新绘制的图形如何与已有的图形进行组合。
// 源图形仅会在与目标图形重叠的区域内显示,并且会覆盖目标图形,而非重叠区域将保持目标图形原有的样式。
ctx.globalCompositeOperation="destination-atop";
ctx.fillStyle="green";
ctx.fillRect(100,100,100,100);

在这里插入图片描述

🔥刮刮卡实例

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no"></meta><title></title><style type="text/css">*{margin: 0;padding: 0;}html,body{height: 100%;overflow: hidden;}#wrap,ul,ul>li{height: 100%;}ul>li{background: url(img/b.png);background-size:100% 100% ;}canvas{position: absolute;left: 0;top: 0;transition:1s;}</style></head><body><div id="wrap"><!-- 画布 --><canvas></canvas><ul><li></li></ul></div></body><script type="text/javascript">window.onload = function() {var canvas = document.querySelector("canvas"); canvas.width = document.documentElement.clientWidth;canvas.height = document.documentElement.clientHeight;if (canvas.getContext) {var ctx = canvas.getContext("2d"); var img = new Image();img.src = "img/a.png"; img.onload = function() { draw(); }//绘制画布function draw() {// 记录透明像素点的数量var flag = 0; // 将图片绘制到画布上ctx.drawImage(img, 0, 0, canvas.width, canvas.height); // 判断是否正在绘制路径(使得刮刮乐更平滑)var isDrawing = false; var lastX = 0; // 上一个绘制点的水平坐标var lastY = 0; // 上一个绘制点的垂直坐标canvas.addEventListener("mousedown", startDrawing); canvas.addEventListener("mousemove", drawPath); canvas.addEventListener("mouseup", stopDrawing); /*** 开始绘制路径* @param {MouseEvent} ev 鼠标按下事件*/function startDrawing(ev) {isDrawing = true; // 获取当前鼠标位置的水平坐标var x = ev.clientX - canvas.offsetLeft; // 获取当前鼠标位置的垂直坐标var y = ev.clientY - canvas.offsetTop; // 记录起始坐标[lastX, lastY] = [x, y]; }/*** 绘制路径* @param {MouseEvent} ev 鼠标移动事件*/function drawPath(ev) {if (!isDrawing) return; var x = ev.clientX - canvas.offsetLeft; var y = ev.clientY - canvas.offsetTop; // 设置绘制模式为“目标图像透明部分显示出来”ctx.globalCompositeOperation = "destination-out";ctx.lineWidth = 40; ctx.lineCap = "round"; ctx.lineJoin = "round"; ctx.save(); ctx.beginPath(); ctx.moveTo(lastX, lastY); ctx.lineTo(x, y); ctx.stroke(); ctx.restore(); [lastX, lastY] = [x, y]; }/*** 停止绘制路径*/function stopDrawing() {isDrawing = false; var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);// 计算像素总数var allPx = imgData.width * imgData.height; for (var i = 0; i < allPx; i++) {// 判断像素是否透明if (imgData.data[4 * i + 3] === 0) { flag++; }}// 如果透明像素点数量超过一半,则将画布透明度设置为0(就认为已经刮得差不多if (flag >= allPx / 2) { canvas.style.opacity = 0;}}// 监听CSS过渡效果结束事件canvas.addEventListener("transitionend", function() { this.remove(); // 移除画布元素})}}}</script>
</html>

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【原创】java+swing+mysql宠物领养管理系统设计与实现

摘要&#xff1a; 生活中&#xff0c;有很多被人遗弃的宠物&#xff0c;这些宠物的处理成为了一个新的难题。生活中也有许多人喜欢养宠物&#xff0c;为了方便大家进行宠物领养&#xff0c;提高宠物领养管理的效率和便利性。本文针对这一问题&#xff0c;提出设计和实现一个基…

什么是NPM(Node Package Manager)?它的作用是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

随笔--解决ubuntu虚拟环境的依赖问题

文章目录 问题一&#xff1a;在conda虚拟环境中报错ImportError: libcupti.so.11.7:cannot open shared object file: No such file or directory解决步骤问题二&#xff1a; RuntimeError: CUDA error: CUBLAS_STATUS_INVALID_VALUE when calling cublasSgemmStridedBatched( …

分布式系统之BASE理论

BASE理论是对分布式系统设计和处理的一种理论指导&#xff0c;相对于ACID&#xff08;原子性、一致性、隔离性和持久性&#xff09;这一强一致性模型&#xff0c;BASE更强调在分布式系统中牺牲强一致性以获得可用性和性能的平衡。 BASE理论的核心概念包括&#xff1a; Basica…

设计模式之迭代器模式

什么是迭代器模式 迭代器模式&#xff08;Iterator pattern&#xff09;是一种对象行为型设计模式&#xff0c;它提供了一种方法来顺序访问聚合对象中的元素&#xff0c;而又不暴露该对象的内部表示&#xff0c;同时也可以将迭代逻辑与聚合对象的实现分离&#xff0c;增强了代码…

「掌握创意,释放想象」——Photoshop 2023,你的无限可能!

Adobe Photoshop 2023(PS2023) 来了,全世界数以百万计的设计师、摄影师和艺术家使用 Photoshop 将不可能变为可能。从海报到包装&#xff0c;从基本的横幅到漂亮的网站&#xff0c;从令人难忘的徽标到引人注目的图标&#xff0c;Photoshop 2023让创意世界不断前进。借助直观的工…

docker安装(超详细)

一.引言 本安装教程参考Docker官方文档&#xff0c;地址如下&#xff1a;https://docs.docker.com/engine/install/centos/ 二.卸载旧版docker(第一次安装可忽略) 首先如果系统中已经存在旧的Docker&#xff0c;则先卸载&#xff1a; yum remove docker \docker-client \docker…

【Go 编程实践】从零到一:创建、测试并发布自己的 Go 库

为什么需要开发自己的 Go 库 在编程语言中&#xff0c;包&#xff08;Package&#xff09;和库&#xff08;Library&#xff09;是代码组织和复用的重要工具。在 Go 中&#xff0c;包是代码的基本组织单位&#xff0c;每个 Go 程序都由包构成。包的作用是帮助组织代码&#xf…

Android---App 的安装过程

Android 系统中两个比较重要的服务 ActivityManagerService(AMS) 和 WindowManagerService(WMS)&#xff0c;这篇文章中通过分析 apk 的安装过程&#xff0c;来了解 Android 中另一个比较重要的系统服务 -- PackageManagerService(PMS)。 编译阶段 在分析安装过程之前&#x…

ubuntu无网络连接,没有网络标识,快速解决方法

在这里插入代码片当我们装虚拟机的时候&#xff0c;需要用到网络时发现没有网络连接&#xff0c;且右上角没有网络标识符&#xff0c;这时只需要简单的输入一下三个命令即可 sudo nmcli networking offsudo nmcli networking onsudo service network-manager restart然后重启客…

windows环境下安装Java过程(免登录Oracle官网下载java)

下载路径 oracle官网&#xff1a; java下载路径 Oracle共享账号可下载JDK&#xff1a; 指路 安装流程 执行下载后的jdk的可执行文件一路next下去&#xff0c; 可以自定义安装路径添加环境变量&#xff0c; 两个地方需要添加 在cmd中输入java -version 进行验证&#xff0c;…

在线实用计算工具大全

在线实用计算工具大全 在线计算工具的实用&#xff0c;可以有效提高学习或工作效率&#xff0c;本博文介绍一个在线的实用计算工具大全&#xff08;https://tool.520101.com&#xff09;&#xff0c;作为一种辅助学习工具。 1. 在线排列组合计算 https://tool.520101.com/cal…

ts和js的区别?

文章目录 前言是什么&#xff1f;二、特性三、区别后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;Typescript &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不断努力填补技术短板。(如果出现…

酷开科技 | 酷开系统里萌萌哒小维在等你!

在一片金黄淡绿的颜色中&#xff0c;深秋的脚步更近了&#xff0c;在这个气候微凉的季节里&#xff0c;你是不是更想拥有一种温暖的陪伴呢&#xff1f;酷开科技智慧AI语音功能更懂你&#xff0c;贴心的小维用心陪伴你的每一天。 01.全天候陪伴 在酷开系统中&#xff0c;只要你…

vue3中router和route的区别(使用场景)

1.router router是用来对路由进行操作的&#xff1b; 多用于路由跳转、路由守卫、页面刷新、给路由文件添加路由路径或者移除路由路径等等 2.route route是用来获取路由信息的&#xff1b; 多用于获取路由路径、路由传参数据、路由文件配置的属性信息等等

链表(1)

目录 单链表 主函数test.c test1 test2 test3 test4 头文件&函数声明SList.h 函数实现SList.c 打印SLPrint 创建节点CreateNode 尾插SLPushBack 头插SLPushFront 头删SLPopBck 尾删SLPopFront 易错点 本篇开始链表学习。今天主要是单链表&OJ题目。 单链…

如何本地搭建SeaFile私有云盘并实现远程连接

文章目录 1. 前言2. SeaFile云盘设置2.1 Owncould的安装环境设置2.2 SeaFile下载安装2.3 SeaFile的配置 3. cpolar内网穿透3.1 Cpolar下载安装3.2 Cpolar的注册3.3 Cpolar云端设置3.4 Cpolar本地设置 4.公网访问测试5.结语 1. 前言 现在我们身边的只能设备越来越多&#xff0c…

高校为什么需要大数据挖掘平台?

目前数据挖掘已经成为各种应用领域的重要技术&#xff0c;大学数据挖掘课程的开放已经出现。数据挖掘课程整合了多门学科知识。该课程包括各种理论知识&#xff0c;也离不开相关的实用技术。整个教学过程是培养和提高学生全面创新和解决问题的能力。过去&#xff0c;教学过程理…

uniapp 微信小程序 uni-file-picker上传图片报错 chooseAndUploadFile

这个问题真的很搞&#xff0c; 原因是微信开发者工具更新了&#xff0c;导致图片上传问题。 解决方法&#xff1a; 将微信开发者工具的基础库改为2.33.0一下即可。 在微信开发者工具详情 - 本地设置中&#xff08;记得点击‘推送’按钮&#xff09;&#xff1a;

中国多主数据库:压强投入,期待破茧

拿破仑曾说&#xff1a;“战争的艺术就是在某一点上集中最大优势兵力”&#xff0c;强调了力量集中的重要性。 如今&#xff0c;国际形势风云变幻&#xff0c;西方世界对中国的围剿不再仅仅体现在军事和地缘政治上&#xff0c;而更多表现在经济与科技上。在科技领域&#xff0…