Interface de développement de la bibliothèque JCanvas
Dernières nouvelles : /content/10088155.html
Copiez le contenu suivant dans un document texte et enregistrez-le sous JCanvas.js
/** * @author 智圆行方 */ /** * DisplayObject 类 * 可视对象(抽象) * 所有的可视对象均继承于此类 * 定义了所有可视对象最基本得五项属性 */ function DisplayObject() { this.x=0;//横坐标 this.y=0;//纵坐标 this.width=0;//宽度 this.height=0;//高度 this.stage=null;//对应的舞台对象(舞台对象对应的舞台对象为null) }; /** * InteractiveObject 类 * 交互对象(抽象) * 所有用来与用户交互的对象均继承于此类 * 定义了相关的事件操作方法、属性 */ function InteractiveObject() { DisplayObject.call(this);//继承 this.eventListner=new Object();//事件侦听器列表 }; InteractiveObject.prototype.addEventListner= function(type,func) {//添加事件侦听器(同一类型的事件可有多个侦听器) if(this.eventListner[type]==null || this.eventListner[type]==undefined) {//如果为空或未定义 this.eventListner[type]=new Array();//定义为数组对象 } this.eventListner[type].push(func);//添加一个事件侦听器到该类型 }; InteractiveObject.prototype.removeEventListner= function(type,func) {//移除事件侦听器(同一类型事件的某一侦听器) if(this.eventListner[type]==null || this.eventListner[type]==undefined) {//如果本来就没有 return;//返回 } for (var i=0; i < this.eventListner[type].length; i++) {//有,就循环 if(this.eventListner[type][i]==func) {//判断一下,哪一个是指定的侦听器函数 delete this.eventListner[type][i];//删除它 this.eventListner[type].splice(i,1);//在数组中移除他 } }; if(this.eventListner[type].length==0) {//如果该类型已经没有侦听器 delete this.eventListner[type];//清除该类型的侦听器对象 } }; InteractiveObject.prototype.removeAllEventListner= function(type) {//移除某类型事件的所有侦听器 if(this.eventListner[type]==null || this.eventListner[type]==undefined) {//本来为空,不理他 return; } this.eventListner[type].splice();//移除 delete this.eventListner[type];//删除 }; InteractiveObject.prototype.hasEventListner= function(type) {//是否有事件侦听器 return (this.eventListner[type]!=null && this.eventListner[type]!=undefined && this.eventListner[type].length>0); }; /** * DisplayObjectContainer 类 * 可视对象容器(具体) * 所有可以拥有子可视对象的对象均继承于此类 * 定义了操作子可视对象的属性、方法 */ function DisplayObjectContainer(ctx) { InteractiveObject.call(this);//继承 this.ctx=ctx;//具体类将涉及到canvas的上下文context,需要提供该参数并记录到属性中 this.childs=new Array();//子对象数组 this.maxWidth=0;//根据子对象宽高记录最大宽高 this.maxHeight=0; this.moveChild=new Array();//当前鼠标悬停子成员数组(由于对象重叠关系,悬停子成员并非一个,所以用数组)。此属性在处理事件时起作用。 }; DisplayObjectContainer.prototype=new InteractiveObject();//继承交互对象的原型 DisplayObjectContainer.prototype.getContext= function() {//取上下文,完全可以直接操作属性 return this.ctx; }; DisplayObjectContainer.prototype.addChild= function(child) {//添加子对象 if(this.maxWidth<child.x+child.width) { this.maxWidth=child.x+child.width;//自动处理最大宽高 } if(this.maxHeight<child.y+child.height) { this.maxHegiht=child.y+child.height; } this.childs.push(child);//加入 child.stage=this;//子对象的舞台stage属性自动赋值 }; DisplayObjectContainer.prototype.addChildAt= function(child,index) {//按照索引添加子对象 if(this.maxWidth<child.x+child.width) { this.maxWidth=child.x+child.width;//自动处理最大宽高 } if(this.maxHeight<child.y+child.height) { this.maxHegiht=child.y+child.height; } this.childs.splice(index,0,child);//在index索引处插入子对象 child.stage=this;//子对象的舞台stage属性自动赋值 }; DisplayObjectContainer.prototype.removeChild= function(child) {//移除子对象 this.childs.splice(this.getChildIndex(child),1);//移除 if(this.maxWidth==child.x+child.width) {//处理最大宽高 this.maxWidth=0; for (var i=0; i < this.childs.length; i++) { if(this.childs[i].x+this.childs[i].width>this.maxWidth) { this.maxWidth=this.childs[i].x+this.childs[i].width; } }; } if(this.maxHeight==child.y+child.height) { this.maxHeight=0; for (var i=0; i < this.childs.length; i++) { if(this.childs[i].y+this.childs[i].height>this.maxHeight) { this.maxHeight=this.childs[i].y+this.childs[i].height; } }; } child.stage=null;//已移除,故子对象舞台stage为空 }; DisplayObjectContainer.prototype.removeChildAt= function(index) {//根据索引移除子对象 this.childs[index].stage=null;//已移除,故子对象舞台stage为空 this.childs.splice(index,1);//移除 if(this.maxWidth==child.x+child.width) {//处理最大宽高 this.maxWidth=0; for (var i=0; i < this.childs.length; i++) { if(this.childs[i].x+this.childs[i].width>this.maxWidth) { this.maxWidth=this.childs[i].x+this.childs[i].width; } }; } if(this.maxHeight==child.y+child.height) { this.maxHeight=0; for (var i=0; i < this.childs.length; i++) { if(this.childs[i].y+this.childs[i].height>this.maxHeight) { this.maxHeight=this.childs[i].y+this.childs[i].height; } }; } }; DisplayObjectContainer.prototype.getChildAt= function(index) {//根据索引取出子对象 return this.childs[index]; }; DisplayObjectContainer.prototype.contains= function(child) {//判断某对象是否为该stage舞台的子对象 return (this.getChildIndex(child)!=-1); }; DisplayObjectContainer.prototype.getChildIndex= function(child) {//根据子对象取出索引 for (var i=0; i < this.childs.length; i++) { if(this.childs[i]==child) { return i; } }; return -1; }; DisplayObjectContainer.prototype.setChildIndex= function(child,index) {//重设子对象索引(未测试) this.removeChild(child); this.addChildAt(child,index); }; DisplayObjectContainer.prototype.swapChildren= function(child1,child2) {//交换子对象索引(未测试) this.setChildIndex(child1,this.getChildIndex(child2)); this.setChildIndex(child2,this.getChildIndex(child1)); }; DisplayObjectContainer.prototype.dispatchMouseEvent= function(type,x,y) {//调度鼠标事件 var mouseX=x; var mouseY=y; var newMoveChild=new Array();//当前鼠标悬浮子对象数组 for (var i=0; i < this.childs.length; i++) { if(this.childs[i].dispatchMouseEvent!=null && this.childs[i].dispatchMouseEvent!=undefined) { this.childs[i].dispatchMouseEvent(type,mouseX-this.childs[i].x,mouseY-this.childs[i].y);//如果子对象仍有调度函数,也需调用 } //↓ 与子对象相交 if(mouseX>this.childs[i].x && mouseX<this.childs[i].x+this.childs[i].width && mouseY>this.childs[i].y && mouseY<this.childs[i].y+this.childs[i].height) { if(type=="onmousemove" ) { newMoveChild.push(this.childs[i]);//如果是鼠标移动事件,记录悬浮对象 } if(this.childs[i].eventListner[type]==null || this.childs[i].eventListner[type]==undefined) { continue;//如果子对象没有相应事件侦听器,跳过 } for (var j=0; j < this.childs[i].eventListner[type].length; j++) { this.childs[i].eventListner[type][j](mouseX-this.childs[i].x,mouseY-this.childs[i].y);//否则循环执行一遍侦听器 } } }; if(type!="onmousemove") { return;//如果不是鼠标移动事件,就无事了 } for (var j=0; j < this.moveChild.length; j++) {//循环寻找 原先悬浮子对象中有,新悬浮子对象中没有的 var has=false; for (var i=0; i < newMoveChild.length; i++) { if(this.moveChild[j]==newMoveChild[i]) { has=true; } }; if(has==false) {//没有了 if(this.moveChild[j].eventListner["onmouseout"]) {//即鼠标移出了它,为他处理onmouseout事件 for (var i=0; i < this.moveChild[j].eventListner["onmouseout"].length; i++) { this.moveChild[j].eventListner["onmouseout"][i](mouseX-this.moveChild[j].x,mouseY-this.moveChild[j].y); } } delete this.moveChild[j]; this.moveChild[j]=undefined; } }; for (var i=0; i < newMoveChild.length; i++) {//循环寻找 新悬浮子对象中有,原悬浮子对象中没有的 var has=false; for (var j=0; j < this.moveChild.length; j++) { if(this.moveChild[j]==newMoveChild[i]) { has=true; } }; if(has==false) {//没有的 this.moveChild.push(newMoveChild[i]);//即新被鼠标碰撞的,处理onmouseover事件 if(newMoveChild[i].eventListner["onmouseover"]) { for (var j=0; j < newMoveChild[i].eventListner["onmouseover"].length; j++) { newMoveChild[i].eventListner["onmouseover"][j](mouseX-newMoveChild[i].x,mouseY-newMoveChild[i].y); } } } }; this.cleanUpMoveChild();//清理悬浮对象数组 }; DisplayObjectContainer.prototype.cleanUpMoveChild= function() { var tempArr=new Array();//临时数组 for(var i=0;i<this.moveChild.length;i++) { if(this.moveChild[i]!=null && this.moveChild[i]!=undefined) {//如果悬浮对象数组中当前元素非空非未定义 tempArr.push(this.moveChild[i]);//加入到临时数组 } } this.moveChild=tempArr;//临时数组赋值给悬浮对象数组 } /** * Stage 类 * 舞台(具体) * 任何一个Canvas只能对应且必须对应一个舞台对象。此类无子类。 * 此类定义了对Canvas的封装的功能 */ function Stage(canvas) {//传递一个canvas对象 this.canvas=canvas;//记录canvas对象 this.hasStart=false;//是否已经启动 DisplayObjectContainer.call(this,this.canvas.getContext("2d"));//继承于可视对象容器类 this.Interval=null;//时钟 this.stage=null;//舞台的stage变量为null(前面说过) this.width=this.canvas.width;//宽高即canvas宽高 this.height=this.canvas.height; //下面开始挂接事件 //鼠标事件 this.canvas.onmousemove= function(param) { return function(event) {//首先进行stage本身的事件处理 if(param.eventListner["onmousemove"]!=null && param.eventListner["onmousemove"]!=undefined) { for (var i=0; i < param.eventListner["onmousemove"].length; i++) { param.eventListner["onmousemove"][i](event.clientX-param.canvas.offsetLeft,event.clientY-param.canvas.offsetTop); } } //然后调度鼠标事件到各个子对象 param.dispatchMouseEvent("onmousemove",event.clientX-param.canvas.offsetLeft,event.clientY-param.canvas.offsetTop); }; }(this); this.canvas.onclick= function(param) { return function(event) {//同理 if(param.eventListner["onclick"]!=null && param.eventListner["onclick"]!=undefined) { for (var i=0; i < param.eventListner["onclick"].length; i++) { param.eventListner["onclick"][i](event.clientX-param.canvas.offsetLeft,event.clientY-param.canvas.offsetTop); } } param.dispatchMouseEvent("onclick",event.clientX-param.canvas.offsetLeft,event.clientY-param.canvas.offsetTop); }; }(this); this.canvas.onmousedown= function(param) { return function(event) { if(param.eventListner["onmousedown"]!=null && param.eventListner["onmousedown"]!=undefined) { for (var i=0; i < param.eventListner["onmousedown"].length; i++) { param.eventListner["onmousedown"][i](event.clientX-param.canvas.offsetLeft,event.clientY-param.canvas.offsetTop); } } param.dispatchMouseEvent("onmousedown",event.clientX-param.canvas.offsetLeft,event.clientY-param.canvas.offsetTop); }; }(this); this.canvas.onmouseup= function(param) { return function(event) { event.clientX-=param.canvas.offsetLeft; event.clientY-=param.canvas.offsetTop; if(param.eventListner["onmouseup"]!=null && param.eventListner["onmouseup"]!=undefined) { for (var i=0; i < param.eventListner["onmouseup"].length; i++) { param.eventListner["onmouseup"][i](event.clientX-param.canvas.offsetLeft,event.clientY-param.canvas.offsetTop); } } param.dispatchMouseEvent("onmouseup",event.clientX-param.canvas.offsetLeft,event.clientY-param.canvas.offsetTop); }; }(this); this.canvas.onmouseover= function(param) { return function(event) { if(param.eventListner["onmouseover"]!=null && param.eventListner["onmouseover"]!=undefined) { for (var i=0; i < param.eventListner["onmouseover"].length; i++) { param.eventListner["onmouseover"][i](event.clientX-param.canvas.offsetLeft,event.clientY-param.canvas.offsetTop); } } param.dispatchMouseEvent("onmouseover",event.clientX-param.canvas.offsetLeft,event.clientY-param.canvas.offsetTop); }; }(this); this.canvas.onmouseout= function(param) { return function(event) { if(param.eventListner["onmouseout"]!=null && param.eventListner["onmouseout"]!=undefined) { for (var i=0; i < param.eventListner["onmouseout"].length; i++) { param.eventListner["onmouseout"][i](event.clientX-param.canvas.offsetLeft,event.clientY-param.canvas.offsetTop); } } param.dispatchMouseEvent("onmouseout",event.clientX-param.canvas.offsetLeft,event.clientY-param.canvas.offsetTop); }; }(this); //键盘事件,只有stage的事情 document.onkeydown= function(param) { return function(event) { if(param.eventListner["onkeydown"]!=null && param.eventListner["onkeydown"]!=undefined) { for (var i=0; i < param.eventListner["onkeydown"].length; i++) { param.eventListner["onkeydown"][i](event); } } }; }(this); document.onkeyup= function(param) { return function(event) { if(param.eventListner["onkeyup"]!=null && param.eventListner["onkeyup"]!=undefined) { for (var i=0; i < param.eventListner["onkeyup"].length; i++) { param.eventListner["onkeyup"][i](event); } } }; }(this); document.onkeypress= function(param) { return function(event) { if(param.eventListner["onkeypress"]!=null && param.eventListner["onkeypress"]!=undefined) { for (var i=0; i < param.eventListner["onkeypress"].length; i++) { param.eventListner["onkeypress"][i](event); } } }; }(this); this.draw= function() {//舞台自身的绘制函数 }; this.onEnterFrame= function() {//单独的舞台的刷新帧事件处理 }; }; Stage.prototype=new DisplayObjectContainer();//继承 Stage.prototype.render= function() {//舞台的渲染函数(所有可视对象都应该有一个render函数) this.ctx.clearRect(0,0,this.width,this.height);//清空canvas this.draw();//画自己 for (var i=0; i < this.childs.length; i++) {//循环绘制子对象 this.ctx.translate(this.childs[i].x,this.childs[i].y);//根据子对象坐标,平移坐标系 this.childs[i].render(); this.ctx.translate(-this.childs[i].x,-this.childs[i].y);//再回来 }; }; Stage.prototype.start= function() {//开始 this.hasStart=true; this.Interval=setInterval((function(param) { return function() { param.render(); param.onEnterFrame(); } })(this),10); }; Stage.prototype.stop= function() {//停止 this.hasStart=false; clearInterval(this.Interval); } /** * Sprite 类 * 精灵(具体) * 拥有一些对图形的操作方法(如拖放) */ function Sprite(ctx) { DisplayObjectContainer.call(this,ctx);//继承 this.isDragging=false;//是否正在拖放 this.dragX=null;//拖放时需要临时使用的坐标变量 this.dragY=null; this.dragFunc=null;//拖放事件处理函数的记录,以便移除 this.stopDragFunc=null;//停止拖放事件处理函数,以便移除 this.draw= function() {//本身的绘制函数 }; }; Sprite.prototype=new DisplayObjectContainer();//继承 Sprite.prototype.render= function() {//必有的渲染函数 this.draw();//画自己 //根据子对象最大宽高决定缩放程度 this.ctx.scale(this.width<this.maxWidth?this.width/this.maxWidth:1,this.height<this.maxHeight?this.height/this.maxHeight:1); for (var i=0; i < this.childs.length; i++) {//循环绘制子对象 this.ctx.translate(this.childs[i].x,this.childs[i].y); this.childs[i].render(); this.ctx.translate(-this.childs[i].x,-this.childs[i].y); }; this.ctx.scale(this.width<this.maxWidth?this.maxWidth/this.width:1,this.height<this.maxHeight?this.maxHeight/this.height:1); }; Sprite.prototype.startDrag= function (startX,startY) {//开始拖放 this.isDragging=true; this.dragX=startX+this.x; this.dragY=startY+this.y; this.dragFunc= function(param) { return function(x,y) { var offsetX=x-param.dragX; var offsetY=y-param.dragY; param.x+=offsetX; param.y+=offsetY; param.dragX=x; param.dragY=y; }; }(this); this.stopDragFunc= function(param) { return function(x,y) { param.stopDrag(); }; }(this); this.stage.addEventListner("onmousemove",this.dragFunc); this.stage.addEventListner("onmouseout",this.stopDragFunc); }; Sprite.prototype.stopDrag= function() {//停止拖放 this.isDragging=false; this.dragY=this.dragX=null; this.stage.removeEventListner("onmousemove",this.dragFunc); delete this.dragFunc; this.stage.removeEventListner("onmouseout",this.stopDragFunc); delete this.stopDragFunc; }; /** * Shape 类 * 图形(具体) * 无任何交互功能 */ function Shape(ctx) { DisplayObject.call(this);//继承 this.ctx=ctx; this.draw= function() {//绘制函数 }; } Shape.prototype.render=function(){//渲染函数,即绘制自己 this.draw(); }; /** * SimpleButton 类 * 普通按钮(具体) * 根据三态图形形成按钮。 */ function SimpleButton(ctx) { InteractiveObject.call(this);//继承 this.upState=new Shape(ctx);//三态图形 this.overState=new Shape(ctx); this.downState=new Shape(ctx); this.curState="up";//当前状态,可为“up” “over” “down” //根据不同事件改变当前状态 this.addEventListner("onmouseover", function(param) { return function(x,y) { param.curState="over"; } }(this)); this.addEventListner("onmousedown", function(param) { return function(x,y) { param.curState="down"; } }(this)); this.addEventListner("onmouseup", function(param) { return function(x,y) { param.curState="up"; } }(this)); this.addEventListner("onmouseout", function(param) { return function(x,y) { param.curState="up"; } }(this)); }; SimpleButton.prototype=new InteractiveObject();//继承 SimpleButton.prototype.render= function() {//渲染 switch(this.curState) {//判断当前状态,进行不同图形绘制 case "up": if(this.upState!=null && this.upState!=undefined) { this.upState.width=this.width; this.upState.height=this.height; this.upState.draw(); } break; case "down": if(this.downState!=null && this.downState!=undefined) { this.downState.width=this.width; this.downState.height=this.height; this.downState.draw(); } break; case "over": if(this.overState!=null && this.overState!=undefined) { this.overState.width=this.width; this.overState.height=this.height; this.overState.draw(); } break; } };
Copiez le contenu suivant dans un document texte et enregistrez-le sous Real-time tracking.htm (doit être placé dans le même répertoire que le fichier ci-dessus).
<!DOCTYPE HTML> <html> <head> <meta charset=UTF-8 /> <title>智圆行方JCanvas库 之 实时追踪</title> <script type="text/javascript" src="JCanvas+.js"></script> </head> <body> <canvas id="myCanvas" width="800" height="600"> 您的浏览器不支持Canvas标签。 IE用户请务必使用IE9(不兼容XP)及以上版本; 其它浏览器用户请尽量使用最新版本浏览器。 </canvas> <script type="text/javascript"> var canvas=document.getElementById("myCanvas");//获得canvas对象 var stage=new Stage(canvas);//一个canvas对象只能对应且必须对应一个Stage类型的实例 var myself=new Sprite(stage.ctx); myself.x=myself.y=50; myself.width=50; myself.height=50; myself.draw= function() { this.ctx.beginPath(); this.ctx.arc(0,0,25,0,Math.PI*2,true); this.ctx.closePath(); this.ctx.fillStyle="black"; this.ctx.fill(); }; var badMan=new Sprite(stage.ctx); badMan.x=500; badMan.y=500; badMan.width=50; badMan.height=50; badMan.draw= function() { this.ctx.beginPath(); this.ctx.arc(0,0,25,0,Math.PI*2,true); this.ctx.closePath(); this.ctx.fillStyle="red"; this.ctx.fill(); }; var btn1=new SimpleButton(stage.ctx); btn1.width=200; btn1.height=50; btn1.upState.draw= function() { this.ctx.fillStyle="blue"; this.ctx.fillRect(0,0,this.width,this.height); }; btn1.downState.draw= function() { this.ctx.fillStyle="black"; this.ctx.fillRect(0,0,this.width,this.height); }; btn1.overState.draw= function() { this.ctx.fillStyle="yellow"; this.ctx.fillRect(0,0,this.width,this.height); }; btn1.addEventListner("onclick",function(){ alert("哈哈!"); }); stage.addChild(myself); stage.addChild(badMan); stage.addChild(btn1); stage.onEnterFrame= function() { badMan.x+=(myself.x-badMan.x)*0.1; badMan.y+=(myself.y-badMan.y)*0.1; }; stage.addEventListner("onmousemove", function(x,y) { myself.x=x; myself.y=y; }); stage.start(); </script> </body> </html>
Ce qui précède est le contenu de l'interface de développement de la bibliothèque JCanvas. Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois (www.php.cn) !

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

L'article discute de l'utilisation de balises Meta pour contrôler la mise à l'échelle des pages sur les appareils mobiles, en se concentrant sur des paramètres tels que la largeur et l'échelle initiale pour une réactivité et des performances optimales. COMMANDE: 159

L'exécution du projet H5 nécessite les étapes suivantes: Installation des outils nécessaires tels que le serveur Web, Node.js, les outils de développement, etc. Créez un environnement de développement, créez des dossiers de projet, initialisez les projets et écrivez du code. Démarrez le serveur de développement et exécutez la commande à l'aide de la ligne de commande. Aperçu du projet dans votre navigateur et entrez l'URL du serveur de développement. Publier des projets, optimiser le code, déployer des projets et configurer la configuration du serveur Web.

L'article traite de la gestion de la confidentialité de l'emplacement des utilisateurs et des autorisations à l'aide de l'API Geolocation, mettant l'accent sur les meilleures pratiques pour demander des autorisations, assurer la sécurité des données et se conformer aux lois sur la confidentialité.

L'article discute de l'utilisation de l'API de visibilité de la page HTML5 pour détecter la visibilité de la page, améliorer l'expérience utilisateur et optimiser l'utilisation des ressources. Les aspects clés comprennent la pause des supports, la réduction de la charge du processeur et la gestion de l'analyse en fonction des changements de visibilité.

L'article explique comment utiliser l'API HTML5 Drag and Drop pour créer des interfaces utilisateur interactives, détaillant les étapes pour rendre les éléments dragables, gérer les événements clés et améliorer l'expérience utilisateur avec des commentaires personnalisés. Il discute également des pièges communs à un

La page H5 doit être maintenue en continu, en raison de facteurs tels que les vulnérabilités du code, la compatibilité des navigateurs, l'optimisation des performances, les mises à jour de sécurité et les améliorations de l'expérience utilisateur. Des méthodes de maintenance efficaces comprennent l'établissement d'un système de test complet, à l'aide d'outils de contrôle de version, de surveiller régulièrement les performances de la page, de collecter les commentaires des utilisateurs et de formuler des plans de maintenance.

La production de pages H5 fait référence à la création de pages Web compatibles compatibles multiplateformes à l'aide de technologies telles que HTML5, CSS3 et JavaScript. Son cœur réside dans le code d'analyse du navigateur, la structure de rendu, le style et les fonctions interactives. Les technologies courantes incluent les effets d'animation, la conception réactive et l'interaction des données. Pour éviter les erreurs, les développeurs doivent être débogués; L'optimisation des performances et les meilleures pratiques incluent l'optimisation du format d'image, la réduction des demandes et les spécifications du code, etc. pour améliorer la vitesse de chargement et la qualité du code.

Cet article explique l'API HTML5 WebSockets pour la communication client-serveur bidirectionnelle en temps réel. Il détaille les implémentations côté client (JavaScript) et côté serveur (Python / Flask), résolvant des défis tels que l'évolutivité, la gestion de l'état, un
