canvas烟花锦集

news/2024/6/24 17:14:26

canvas可以实现不同动画效果,本文主要记录几种不同节日烟花效果实现。

原文链接

实现一

firework

效果地址

html

<canvas id="canvas"></canvas>

css

body {background: #000;margin: 0;
}canvas {cursor: crosshair;display: block;
}

js

// when animating on canvas, it is best to use requestAnimationFrame instead of setTimeout or setInterval
// not supported in all browsers though and sometimes needs a prefix, so we need a shim
window.requestAnimFrame = (function () {return window.requestAnimationFrame ||window.webkitRequestAnimationFrame ||window.mozRequestAnimationFrame ||function (callback) {window.setTimeout(callback, 1000 / 60);};
})();// now we will setup our basic variables for the demo
var canvas = document.getElementById('canvas'),ctx = canvas.getContext('2d'),// full screen dimensionscw = window.innerWidth,ch = window.innerHeight,// firework collectionfireworks = [],// particle collectionparticles = [],// starting huehue = 120,// when launching fireworks with a click, too many get launched at once without a limiter, one launch per 5 loop tickslimiterTotal = 5,limiterTick = 0,// this will time the auto launches of fireworks, one launch per 80 loop tickstimerTotal = 80,timerTick = 0,mousedown = false,// mouse x coordinate,mx,// mouse y coordinatemy;// set canvas dimensions
canvas.width = cw;
canvas.height = ch;// now we are going to setup our function placeholders for the entire demo// get a random number within a range
function random(min, max) {return Math.random() * (max - min) + min;
}// calculate the distance between two points
function calculateDistance(p1x, p1y, p2x, p2y) {var xDistance = p1x - p2x,yDistance = p1y - p2y;return Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2));
}// create firework
function Firework(sx, sy, tx, ty) {// actual coordinatesthis.x = sx;this.y = sy;// starting coordinatesthis.sx = sx;this.sy = sy;// target coordinatesthis.tx = tx;this.ty = ty;// distance from starting point to targetthis.distanceToTarget = calculateDistance(sx, sy, tx, ty);this.distanceTraveled = 0;// track the past coordinates of each firework to create a trail effect, increase the coordinate count to create more prominent trailsthis.coordinates = [];this.coordinateCount = 3;// populate initial coordinate collection with the current coordinateswhile (this.coordinateCount--) {this.coordinates.push([this.x, this.y]);}this.angle = Math.atan2(ty - sy, tx - sx);this.speed = 2;this.acceleration = 1.05;this.brightness = random(50, 70);// circle target indicator radiusthis.targetRadius = 1;
}// update firework
Firework.prototype.update = function (index) {// remove last item in coordinates arraythis.coordinates.pop();// add current coordinates to the start of the arraythis.coordinates.unshift([this.x, this.y]);// cycle the circle target indicator radiusif (this.targetRadius < 8) {this.targetRadius += 0.3;} else {this.targetRadius = 1;}// speed up the fireworkthis.speed *= this.acceleration;// get the current velocities based on angle and speedvar vx = Math.cos(this.angle) * this.speed,vy = Math.sin(this.angle) * this.speed;// how far will the firework have traveled with velocities applied?this.distanceTraveled = calculateDistance(this.sx, this.sy, this.x + vx, this.y + vy);// if the distance traveled, including velocities, is greater than the initial distance to the target, then the target has been reachedif (this.distanceTraveled >= this.distanceToTarget) {createParticles(this.tx, this.ty);// remove the firework, use the index passed into the update function to determine which to removefireworks.splice(index, 1);} else {// target not reached, keep travelingthis.x += vx;this.y += vy;}
}// draw firework
Firework.prototype.draw = function () {ctx.beginPath();// move to the last tracked coordinate in the set, then draw a line to the current x and yctx.moveTo(this.coordinates[this.coordinates.length - 1][0], this.coordinates[this.coordinates.length - 1][1]);ctx.lineTo(this.x, this.y);ctx.strokeStyle = 'hsl(' + hue + ', 100%, ' + this.brightness + '%)';ctx.stroke();ctx.beginPath();// draw the target for this firework with a pulsing circlectx.arc(this.tx, this.ty, this.targetRadius, 0, Math.PI * 2);ctx.stroke();
}// create particle
function Particle(x, y) {this.x = x;this.y = y;// track the past coordinates of each particle to create a trail effect, increase the coordinate count to create more prominent trailsthis.coordinates = [];this.coordinateCount = 5;while (this.coordinateCount--) {this.coordinates.push([this.x, this.y]);}// set a random angle in all possible directions, in radiansthis.angle = random(0, Math.PI * 2);this.speed = random(1, 10);// friction will slow the particle downthis.friction = 0.95;// gravity will be applied and pull the particle downthis.gravity = 1;// set the hue to a random number +-20 of the overall hue variablethis.hue = random(hue - 20, hue + 20);this.brightness = random(50, 80);this.alpha = 1;// set how fast the particle fades outthis.decay = random(0.015, 0.03);
}// update particle
Particle.prototype.update = function (index) {// remove last item in coordinates arraythis.coordinates.pop();// add current coordinates to the start of the arraythis.coordinates.unshift([this.x, this.y]);// slow down the particlethis.speed *= this.friction;// apply velocitythis.x += Math.cos(this.angle) * this.speed;this.y += Math.sin(this.angle) * this.speed + this.gravity;// fade out the particlethis.alpha -= this.decay;// remove the particle once the alpha is low enough, based on the passed in indexif (this.alpha <= this.decay) {particles.splice(index, 1);}
}// draw particle
Particle.prototype.draw = function () {ctx.beginPath();// move to the last tracked coordinates in the set, then draw a line to the current x and yctx.moveTo(this.coordinates[this.coordinates.length - 1][0], this.coordinates[this.coordinates.length - 1][1]);ctx.lineTo(this.x, this.y);ctx.strokeStyle = 'hsla(' + this.hue + ', 100%, ' + this.brightness + '%, ' + this.alpha + ')';ctx.stroke();}// create particle group/explosion
function createParticles(x, y) {// increase the particle count for a bigger explosion, beware of the canvas performance hit with the increased particles thoughvar particleCount = 30;while (particleCount--) {particles.push(new Particle(x, y));}
}// main demo loop
function loop() {// this function will run endlessly with requestAnimationFramerequestAnimFrame(loop);// increase the hue to get different colored fireworks over timehue += 0.5;// normally, clearRect() would be used to clear the canvas// we want to create a trailing effect though// setting the composite operation to destination-out will allow us to clear the canvas at a specific opacity, rather than wiping it entirelyctx.globalCompositeOperation = 'destination-out';// decrease the alpha property to create more prominent trailsctx.fillStyle = 'rgba(0, 0, 0, 0.5)';ctx.fillRect(0, 0, cw, ch);// change the composite operation back to our main mode// lighter creates bright highlight points as the fireworks and particles overlap each otherctx.globalCompositeOperation = 'lighter';var text = "HAPPY NEW YEAR !";ctx.font = "50px sans-serif";var textData = ctx.measureText(text);ctx.fillStyle = "rgba(" + parseInt(random(0, 255)) + "," + parseInt(random(0, 255)) + "," + parseInt(random(0,255)) + ",0.3)";ctx.fillText(text, cw / 2 - textData.width / 2, ch / 2);// loop over each firework, draw it, update itvar i = fireworks.length;while (i--) {fireworks[i].draw();fireworks[i].update(i);}// loop over each particle, draw it, update itvar i = particles.length;while (i--) {particles[i].draw();particles[i].update(i);}// launch fireworks automatically to random coordinates, when the mouse isn't downif (timerTick >= timerTotal) {if (!mousedown) {// start the firework at the bottom middle of the screen, then set the random target coordinates, the random y coordinates will be set within the range of the top half of the screenfor (var h = 0; h < 50; h++) {fireworks.push(new Firework(cw / 2, ch / 2, random(0, cw), random(0, ch)));}timerTick = 0;}} else {timerTick++;}// limit the rate at which fireworks get launched when mouse is downif (limiterTick >= limiterTotal) {if (mousedown) {// start the firework at the bottom middle of the screen, then set the current mouse coordinates as the targetfireworks.push(new Firework(cw / 2, ch / 2, mx, my));limiterTick = 0;}} else {limiterTick++;}
}// mouse event bindings
// update the mouse coordinates on mousemove
canvas.addEventListener('mousemove', function (e) {mx = e.pageX - canvas.offsetLeft;my = e.pageY - canvas.offsetTop;
});// toggle mousedown state and prevent canvas from being selected
canvas.addEventListener('mousedown', function (e) {e.preventDefault();mousedown = true;
});canvas.addEventListener('mouseup', function (e) {e.preventDefault();mousedown = false;
});// once the window loads, we are ready for some fireworks!
window.onload = loop;

实现二

firework
效果地址

html

<canvas></canvas>
<h1>201<span>8</span></h1>

css

html,
body {padding: 0px;margin: 0px;background: #222;font-family: 'Karla', sans-serif;color: #FFF;height: 100%;overflow: hidden;
}h1 {z-index: 1000;position: fixed;top: 50%;left: 50%;transform: translateX(-50%) translateY(-100%);font-size: 58px;overflow: hidden;
}span {position: relative;display: inline-block;animation: drop 0.75s ease 0s;
}canvas {width: 100%;height: 100%;
}@keyframes drop {0% {transform: translateY(-100px);opacity: 0;}90% {opacity: 1;transform: translateY(10px);}100% {transform: translateY(0px);}
}

js

var ctx = document.querySelector('canvas').getContext('2d')
ctx.canvas.width = window.innerWidth
ctx.canvas.height = window.innerHeightvar sparks = []
var fireworks = []
var i = 20;
while (i--) {fireworks.push(new Firework(Math.random() * window.innerWidth, window.innerHeight * Math.random()))
}render()function render() {setTimeout(render, 1000 / 60)ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height)for (var firework of fireworks) {if (firework.dead) continuefirework.move()firework.draw()}for (var spark of sparks) {if (spark.dead) continuespark.move()spark.draw()}if (Math.random() < 0.05) {fireworks.push(new Firework())}
}function Spark(x, y, color) {this.x = xthis.y = ythis.dir = Math.random() * (Math.PI * 2)this.dead = falsethis.color = colorthis.speed = Math.random() * 3 + 3;this.walker = new Walker({radius: 20,speed: 0.25})this.gravity = 0.25this.dur = this.speed / 0.1this.move = function () {this.dur--if (this.dur < 0) this.dead = trueif (this.speed < 0) returnif (this.speed > 0) this.speed -= 0.1var walk = this.walker.step()this.x += Math.cos(this.dir + walk) * this.speedthis.y += Math.sin(this.dir + walk) * this.speedthis.y += this.gravitythis.gravity += 0.05}this.draw = function () {drawCircle(this.x, this.y, 3, this.color)}
}function Firework(x, y) {this.xmove = new Walker({radius: 10,speed: 0.5})this.x = x || Math.random() * ctx.canvas.widththis.y = y || ctx.canvas.heightthis.height = Math.random() * ctx.canvas.height / 2this.dead = falsethis.color = randomColor()this.move = function () {this.x += this.xmove.step()if (this.y > this.height) this.y -= 1;else this.burst()}this.draw = function () {drawCircle(this.x, this.y, 1, this.color)}this.burst = function () {this.dead = truevar i = 100;while (i--) sparks.push(new Spark(this.x, this.y, this.color))}
}function drawCircle(x, y, radius, color) {color = color || '#FFF'ctx.fillStyle = colorctx.fillRect(x - radius / 2, y - radius / 2, radius, radius)
}function randomColor() {return ['#6ae5ab', '#88e3b2', '#36b89b', '#7bd7ec', '#66cbe1'][Math.floor(Math.random() * 5)];
}function Walker(options) {this.step = function () {this.direction = Math.sign(this.target) * this.speedthis.value += this.directionthis.target ?this.target -= this.direction :(this.value) ?(this.wander) ?this.target = this.newTarget() :this.target = -this.value :this.target = this.newTarget()return this.direction}this.newTarget = function () {return Math.round(Math.random() * (this.radius * 2) - this.radius)}this.start = 0this.value = 0this.radius = options.radiusthis.target = this.newTarget()this.direction = Math.sign(this.target)this.wander = options.wanderthis.speed = options.speed || 1
}

实现三

firework
效果地址

html

<canvas id="cas" style="background-color:rgba(0,5,24,1)" width="1235" height="680">浏览器不支持canvas</canvas><div class="city"><img src="city.png" alt="">
</div>
<img src="moon.png" alt="" id="moon" style="visibility: hidden;">
<div style="display:none"><div class="shape">新年快乐</div><div class="shape">阖家欢乐</div><div class="shape">万事如意</div><div class="shape">心想事成</div>
</div>

css

body {margin: 0;padding: 0;overflow: hidden;
}.city {width: 100%;position: fixed;bottom: 0px;z-index: 100;
}.city img {width: 100%;
}    

js

var canvas = document.getElementById("cas");
var ocas = document.createElement("canvas");
var octx = ocas.getContext("2d");
var ctx = canvas.getContext("2d");
ocas.width = canvas.width = window.innerWidth;
ocas.height = canvas.height = window.innerHeight;
var bigbooms = [];window.onload = function () {initAnimate()
}function initAnimate() {drawBg();lastTime = new Date();animate();
}var lastTime;function animate() {ctx.save();ctx.fillStyle = "rgba(0,5,24,0.1)";ctx.fillRect(0, 0, canvas.width, canvas.height);ctx.restore();var newTime = new Date();if (newTime - lastTime > 500 + (window.innerHeight - 767) / 2) {var random = Math.random() * 100 > 2 ? true : false;var x = getRandom(canvas.width / 5, canvas.width * 4 / 5);var y = getRandom(50, 200);if (random) {var bigboom = new Boom(getRandom(canvas.width / 3, canvas.width * 2 / 3), 2, "#FFF", {x: x,y: y});bigbooms.push(bigboom)} else {var bigboom = new Boom(getRandom(canvas.width / 3, canvas.width * 2 / 3), 2, "#FFF", {x: canvas.width / 2,y: 200}, document.querySelectorAll(".shape")[parseInt(getRandom(0, document.querySelectorAll(".shape").length))]);bigbooms.push(bigboom)}lastTime = newTime;}stars.foreach(function () {this.paint();})drawMoon();bigbooms.foreach(function (index) {var that = this;if (!this.dead) {this._move();this._drawLight();} else {this.booms.foreach(function (index) {if (!this.dead) {this.moveTo(index);} else if (index === that.booms.length - 1) {bigbooms[bigbooms.indexOf(that)] = null;}})}});raf(animate);
}function drawMoon() {var moon = document.getElementById("moon");var centerX = canvas.width - 200,centerY = 100,width = 80;if (moon.complete) {ctx.drawImage(moon, centerX, centerY, width, width)} else {moon.onload = function () {ctx.drawImage(moon, centerX, centerY, width, width)}}var index = 0;for (var i = 0; i < 10; i++) {ctx.save();ctx.beginPath();ctx.arc(centerX + width / 2, centerY + width / 2, width / 2 + index, 0, 2 * Math.PI);ctx.fillStyle = "rgba(240,219,120,0.005)";index += 2;ctx.fill();ctx.restore();}}Array.prototype.foreach = function (callback) {for (var i = 0; i < this.length; i++) {if (this[i] !== null) callback.apply(this[i], [i])}
}var raf = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame ||window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {window.setTimeout(callback, 1000 / 60);};canvas.onclick = function () {var x = event.clientX;var y = event.clientY;var bigboom = new Boom(getRandom(canvas.width / 3, canvas.width * 2 / 3), 2, "#FFF", {x: x,y: y});bigbooms.push(bigboom)
}var Boom = function (x, r, c, boomArea, shape) {this.booms = [];this.x = x;this.y = (canvas.height + r);this.r = r;this.c = c;this.shape = shape || false;this.boomArea = boomArea;this.theta = 0;this.dead = false;this.ba = parseInt(getRandom(80, 200));
}
Boom.prototype = {_paint: function () {ctx.save();ctx.beginPath();ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);ctx.fillStyle = this.c;ctx.fill();ctx.restore();},_move: function () {var dx = this.boomArea.x - this.x,dy = this.boomArea.y - this.y;this.x = this.x + dx * 0.01;this.y = this.y + dy * 0.01;if (Math.abs(dx) <= this.ba && Math.abs(dy) <= this.ba) {if (this.shape) {this._shapBoom();} else this._boom();this.dead = true;} else {this._paint();}},_drawLight: function () {ctx.save();ctx.fillStyle = "rgba(255,228,150,0.3)";ctx.beginPath();ctx.arc(this.x, this.y, this.r + 3 * Math.random() + 1, 0, 2 * Math.PI);ctx.fill();ctx.restore();},_boom: function () {var fragNum = getRandom(30, 200);var style = getRandom(0, 10) >= 5 ? 1 : 2;var color;if (style === 1) {color = {a: parseInt(getRandom(128, 255)),b: parseInt(getRandom(128, 255)),c: parseInt(getRandom(128, 255))}}var fanwei = parseInt(getRandom(300, 400));for (var i = 0; i < fragNum; i++) {if (style === 2) {color = {a: parseInt(getRandom(128, 255)),b: parseInt(getRandom(128, 255)),c: parseInt(getRandom(128, 255))}}var a = getRandom(-Math.PI, Math.PI);var x = getRandom(0, fanwei) * Math.cos(a) + this.x;var y = getRandom(0, fanwei) * Math.sin(a) + this.y;var radius = getRandom(0, 2)var frag = new Frag(this.x, this.y, radius, color, x, y);this.booms.push(frag);}},_shapBoom: function () {var that = this;putValue(ocas, octx, this.shape, 5, function (dots) {var dx = canvas.width / 2 - that.x;var dy = canvas.height / 2 - that.y;for (var i = 0; i < dots.length; i++) {color = {a: dots[i].a,b: dots[i].b,c: dots[i].c}var x = dots[i].x;var y = dots[i].y;var radius = 1;var frag = new Frag(that.x, that.y, radius, color, x - dx, y - dy);that.booms.push(frag);}})}
}function putValue(canvas, context, ele, dr, callback) {context.clearRect(0, 0, canvas.width, canvas.height);var img = new Image();if (ele.innerHTML.indexOf("img") >= 0) {img.src = ele.getElementsByTagName("img")[0].src;imgload(img, function () {context.drawImage(img, canvas.width / 2 - img.width / 2, canvas.height / 2 - img.width / 2);dots = getimgData(canvas, context, dr);callback(dots);})} else {var text = ele.innerHTML;context.save();var fontSize = 200;context.font = fontSize + "px 宋体 bold";context.textAlign = "center";context.textBaseline = "middle";context.fillStyle = "rgba(" + parseInt(getRandom(128, 255)) + "," + parseInt(getRandom(128, 255)) + "," +parseInt(getRandom(128, 255)) + " , 1)";context.fillText(text, canvas.width / 2, canvas.height / 2);context.restore();dots = getimgData(canvas, context, dr);callback(dots);}
}function imgload(img, callback) {if (img.complete) {callback.call(img);} else {img.onload = function () {callback.call(this);}}
}function getimgData(canvas, context, dr) {var imgData = context.getImageData(0, 0, canvas.width, canvas.height);context.clearRect(0, 0, canvas.width, canvas.height);var dots = [];for (var x = 0; x < imgData.width; x += dr) {for (var y = 0; y < imgData.height; y += dr) {var i = (y * imgData.width + x) * 4;if (imgData.data[i + 3] > 128) {var dot = {x: x,y: y,a: imgData.data[i],b: imgData.data[i + 1],c: imgData.data[i + 2]};dots.push(dot);}}}return dots;
}function getRandom(a, b) {return Math.random() * (b - a) + a;
}var maxRadius = 1,stars = [];function drawBg() {for (var i = 0; i < 100; i++) {var r = Math.random() * maxRadius;var x = Math.random() * canvas.width;var y = Math.random() * 2 * canvas.height - canvas.height;var star = new Star(x, y, r);stars.push(star);star.paint()}}var Star = function (x, y, r) {this.x = x;this.y = y;this.r = r;
}
Star.prototype = {paint: function () {ctx.save();ctx.beginPath();ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);ctx.fillStyle = "rgba(255,255,255," + this.r + ")";ctx.fill();ctx.restore();}
}var focallength = 250;
var Frag = function (centerX, centerY, radius, color, tx, ty) {this.tx = tx;this.ty = ty;this.x = centerX;this.y = centerY;this.dead = false;this.centerX = centerX;this.centerY = centerY;this.radius = radius;this.color = color;
}Frag.prototype = {paint: function () {ctx.save();ctx.beginPath();ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);ctx.fillStyle = "rgba(" + this.color.a + "," + this.color.b + "," + this.color.c + ",1)";ctx.fill()ctx.restore();},moveTo: function (index) {this.ty = this.ty + 0.3;var dx = this.tx - this.x,dy = this.ty - this.y;this.x = Math.abs(dx) < 0.1 ? this.tx : (this.x + dx * 0.1);this.y = Math.abs(dy) < 0.1 ? this.ty : (this.y + dy * 0.1);if (dx === 0 && Math.abs(dy) <= 80) {this.dead = true;}this.paint();}
}

总结

几种不同canvas实现烟花动画,基本分为烟火类和碎屑类展现烟花的不同状态。
同时,祝大家新年快乐!


http://lihuaxi.xjx100.cn/news/237697.html

相关文章

fiddler使用技巧进阶,如何抓包修改数据?——AutoResponder重定向

“ 介绍Fiddler的AutoResponder重定向功能。” Fiddler功能十分强大&#xff0c;既能抓取报文&#xff0c;也能构造报文&#xff0c;本文继续介绍fiddler的功能&#xff0c;这次的功能与构造报文相关&#xff0c;用来回答标题中的疑问&#xff0c;即修改数据的方法&#xff0c;…

BCH下周硬分叉升级 Schnorr签名功能终于来临

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自https://www.liankexing.com&#xff0c;未经允许拒绝转载。 比特币现金&#xff08;Bitcoin Cash&#xff09;网络计划于5月15日进行分叉&#xff0c;社区此前一直在为这次升级做准备&#xff0c;…

Docker 宿主机定时清除容器的运行日志

为什么80%的码农都做不了架构师&#xff1f;>>> docker 宿主机定时清除容器的运行日志 一般docker容器都是最小化安装&#xff0c;不仅如此系统定时器相关的服务也不存在&#xff0c;自己去安装也很麻烦&#xff0c;故此直接使用宿主机的定时器即可。 一、在容器中…

fcm和firebase_我如何最终使Netlify Functions,Firebase和GraphQL一起工作

fcm和firebaseIn a previous post I confessed defeat in attempting to get an AWS Lambda GraphQL server to connect to a Firebase server. I didn’t give up right away, though, and a short time later found a different Node package to achieve what I couldn’t be…

Spark笔试

1.Spark 的四大组件下面哪个不是 (D ) A.Spark Streaming B Mlib C Graphx D Spark R 2.下面哪个端口不是 spark 自带服务的端口 (C ) A.8080 B.4040 C.8090 D.18080 3.spark 1.4 版本的最大变化 (B ) A spark sql Release 版本 B 引入 Spark R C DataFrame D支持动态资源…

Android逆向之调试smali代码基础

点击上方↑↑↑蓝字[协议分析与还原]关注我们 “ 介绍Android逆向中调试smali代码的方法。” 最近在重整Android逆向分析环境&#xff0c;一切都在从零开始&#xff0c;做下记录&#xff0c;给大家分享。 本文介绍Android逆向中smali代码的调试及环境的准备。 事先准备如下工具…

智能合约语言Solidity - 错误处理

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自https://www.liankexing.com&#xff0c;未经允许拒绝转载。 Solidity 是以太坊智能合约编程语言&#xff0c;阅读本文前&#xff0c;你应该对以太坊、智能合约有所了解&#xff0c;如果你还不了解…

Docker网络解决方案-Flannel部署记录

Docker跨主机容器间网络通信实现的工具有Pipework、Flannel、Weave、Open vSwitch&#xff08;虚拟交换机&#xff09;、Calico实现跨主机容器间的通信。其中Pipework、Weave、Flannel&#xff0c;三者的区别是&#xff1a; Weave的思路 12在每个宿主机上布置一个特殊的route的…