CINXE.COM

绘图 - 学习 Web 开发 | MDN

<!doctype html><html lang="en-US" prefix="og: https://ogp.me/ns#"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><link rel="icon" href="https://developer.mozilla.org/favicon-48x48.bc390275e955dacb2e65.png"/><link rel="apple-touch-icon" href="https://developer.mozilla.org/apple-touch-icon.528534bba673c38049c2.png"/><meta name="theme-color" content="#ffffff"/><link rel="manifest" href="https://developer.mozilla.org/manifest.f42880861b394dd4dc9b.json"/><link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="MDN Web Docs"/><title>绘图 - 学习 Web 开发 | MDN</title><link rel="alternate" title="Drawing graphics" href="https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics" hrefLang="en"/><link rel="alternate" title="Zeichnen von Grafiken" href="https://developer.mozilla.org/de/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics" hrefLang="de"/><link rel="alternate" title="Dessiner des graphismes" href="https://developer.mozilla.org/fr/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics" hrefLang="fr"/><link rel="alternate" title="グラフィックの描画" href="https://developer.mozilla.org/ja/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics" hrefLang="ja"/><link rel="alternate" title="绘图" href="https://developer.mozilla.org/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics" hrefLang="zh"/><link rel="preload" as="font" type="font/woff2" href="/static/media/Inter.var.c2fe3cb2b7c746f7966a.woff2" crossorigin=""/><link rel="alternate" type="application/rss+xml" title="MDN Blog RSS Feed" href="https://developer.mozilla.org/en-US/blog/rss.xml" hrefLang="en"/><meta name="description" content="浏览器包含一些非常强大的图形编程工具,从可缩放矢量图形(Scalable Vector Graphics,简称 SVG)语言到用于在 HTML &lt;canvas&gt; 元素上绘制图形的 API(参见 Canvas API 和 WebGL)。本文将介绍 canvas,并提供更多的学习资源。"/><meta property="og:url" content="https://developer.mozilla.org/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics"/><meta property="og:title" content="绘图 - 学习 Web 开发 | MDN"/><meta property="og:type" content="website"/><meta property="og:locale" content="zh_CN"/><meta property="og:description" content="浏览器包含一些非常强大的图形编程工具,从可缩放矢量图形(Scalable Vector Graphics,简称 SVG)语言到用于在 HTML &lt;canvas&gt; 元素上绘制图形的 API(参见 Canvas API 和 WebGL)。本文将介绍 canvas,并提供更多的学习资源。"/><meta property="og:image" content="https://developer.mozilla.org/mdn-social-share.d893525a4fb5fb1f67a2.png"/><meta property="og:image:type" content="image/png"/><meta property="og:image:height" content="1080"/><meta property="og:image:width" content="1920"/><meta property="og:image:alt" content="The MDN Web Docs logo, featuring a blue accent color, displayed on a solid black background."/><meta property="og:site_name" content="MDN Web Docs"/><meta name="twitter:card" content="summary_large_image"/><meta name="twitter:creator" content="MozDevNet"/><link rel="canonical" href="https://developer.mozilla.org/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics"/><style media="print">.article-actions-container,.document-toc-container,.language-menu,.main-menu-toggle,.on-github,.page-footer,.place,.sidebar,.top-banner,.top-navigation-main,ul.prev-next{display:none!important}.main-page-content,.main-page-content pre{padding:2px}.main-page-content pre{border-left-width:2px}</style><script src="/static/js/gtag.js" defer=""></script><script defer="" src="/static/js/main.f565372a.js"></script><link href="/static/css/main.3d9e7a02.css" rel="stylesheet"/></head><body><script>if(document.body.addEventListener("load",(t=>{t.target.classList.contains("interactive")&&t.target.setAttribute("data-readystate","complete")}),{capture:!0}),window&&document.documentElement){const t={light:"#ffffff",dark:"#1b1b1b"};try{const e=window.localStorage.getItem("theme");e&&(document.documentElement.className=e,document.documentElement.style.backgroundColor=t[e]);const o=window.localStorage.getItem("nop");o&&(document.documentElement.dataset.nop=o)}catch(t){console.warn("Unable to read theme from localStorage",t)}}</script><div id="root"><ul id="nav-access" class="a11y-nav"><li><a id="skip-main" href="#content">Skip to main content</a></li><li><a id="skip-search" href="#top-nav-search-input">Skip to search</a></li><li><a id="skip-select-language" href="#languages-switcher-button">Skip to select language</a></li></ul><div class="page-wrapper category-api document-page"><div class="top-banner loading"><section class="place top container"></section></div><div class="sticky-header-container"><header class="top-navigation "><div class="container "><div class="top-navigation-wrap"><a href="/zh-CN/" class="logo" aria-label="MDN homepage"><svg id="mdn-docs-logo" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 694.9 104.4" style="enable-background:new 0 0 694.9 104.4" xml:space="preserve" role="img"><title>MDN Web Docs</title><path d="M40.3 0 11.7 92.1H0L28.5 0h11.8zm10.4 0v92.1H40.3V0h10.4zM91 0 62.5 92.1H50.8L79.3 0H91zm10.4 0v92.1H91V0h10.4z" class="logo-m"></path><path d="M627.9 95.6h67v8.8h-67v-8.8z" class="logo-_"></path><path d="M367 42h-4l-10.7 30.8h-5.5l-10.8-26h-.4l-10.5 26h-5.2L308.7 42h-3.8v-5.6H323V42h-6.5l6.8 20.4h.4l10.3-26h4.7l11.2 26h.5l5.7-20.3h-6.2v-5.6H367V42zm34.9 20c-.4 3.2-2 5.9-4.7 8.2-2.8 2.3-6.5 3.4-11.3 3.4-5.4 0-9.7-1.6-13.1-4.7-3.3-3.2-5-7.7-5-13.7 0-5.7 1.6-10.3 4.7-14s7.4-5.5 12.9-5.5c5.1 0 9.1 1.6 11.9 4.7s4.3 6.9 4.3 11.3c0 1.5-.2 3-.5 4.7h-25.6c.3 7.7 4 11.6 10.9 11.6 2.9 0 5.1-.7 6.5-2 1.5-1.4 2.5-3 3-4.9l6 .9zM394 51.3c.2-2.4-.4-4.7-1.8-6.9s-3.8-3.3-7-3.3c-3.1 0-5.3 1-6.9 3-1.5 2-2.5 4.4-2.8 7.2H394zm51 2.4c0 5-1.3 9.5-4 13.7s-6.9 6.2-12.7 6.2c-6 0-10.3-2.2-12.7-6.7-.1.4-.2 1.4-.4 2.9s-.3 2.5-.4 2.9h-7.3c.3-1.7.6-3.5.8-5.3.3-1.8.4-3.7.4-5.5V22.3h-6v-5.6H416v27c1.1-2.2 2.7-4.1 4.7-5.7 2-1.6 4.8-2.4 8.4-2.4 4.6 0 8.4 1.6 11.4 4.7 3 3.2 4.5 7.6 4.5 13.4zm-7.7.6c0-4.2-1-7.4-3-9.5-2-2.2-4.4-3.3-7.4-3.3-3.4 0-6 1.2-8 3.7-1.9 2.4-2.9 5-3 7.7V57c0 3 1 5.6 3 7.7s4.5 3.1 7.6 3.1c3.6 0 6.3-1.3 8.1-3.9 1.8-2.7 2.7-5.9 2.7-9.6zm69.2 18.5h-13.2v-7.2c-1.2 2.2-2.8 4.1-4.9 5.6-2.1 1.6-4.8 2.4-8.3 2.4-4.8 0-8.7-1.6-11.6-4.9-2.9-3.2-4.3-7.7-4.3-13.3 0-5 1.3-9.6 4-13.7 2.6-4.1 6.9-6.2 12.8-6.2 5.7 0 9.8 2.2 12.3 6.5V22.3h-8.6v-5.6h15.8v50.6h6v5.5zM493.2 56v-4.4c-.1-3-1.2-5.5-3.2-7.3s-4.4-2.8-7.2-2.8c-3.6 0-6.3 1.3-8.2 3.9-1.9 2.6-2.8 5.8-2.8 9.6 0 4.1 1 7.3 3 9.5s4.5 3.3 7.4 3.3c3.2 0 5.8-1.3 7.8-3.8 2.1-2.6 3.1-5.3 3.2-8zm53.1-1.4c0 5.6-1.8 10.2-5.3 13.7s-8.2 5.3-13.9 5.3-10.1-1.7-13.4-5.1c-3.3-3.4-5-7.9-5-13.5 0-5.3 1.6-9.9 4.7-13.7 3.2-3.8 7.9-5.7 14.2-5.7s11 1.9 14.1 5.7c3 3.7 4.6 8.1 4.6 13.3zm-7.7-.2c0-4-1-7.2-3-9.5s-4.8-3.5-8.2-3.5c-3.6 0-6.4 1.2-8.3 3.7s-2.9 5.6-2.9 9.5c0 3.7.9 6.8 2.8 9.4 1.9 2.6 4.6 3.9 8.3 3.9 3.6 0 6.4-1.3 8.4-3.8 1.9-2.6 2.9-5.8 2.9-9.7zm45 5.8c-.4 3.2-1.9 6.3-4.4 9.1-2.5 2.9-6.4 4.3-11.8 4.3-5.2 0-9.4-1.6-12.6-4.8-3.2-3.2-4.8-7.7-4.8-13.7 0-5.5 1.6-10.1 4.7-13.9 3.2-3.8 7.6-5.7 13.2-5.7 2.3 0 4.6.3 6.7.8 2.2.5 4.2 1.5 6.2 2.9l1.5 9.5-5.9.7-1.3-6.1c-2.1-1.2-4.5-1.8-7.2-1.8-3.5 0-6.1 1.2-7.7 3.7-1.7 2.5-2.5 5.7-2.5 9.6 0 4.1.9 7.3 2.7 9.5 1.8 2.3 4.4 3.4 7.8 3.4 5.2 0 8.2-2.9 9.2-8.8l6.2 1.3zm34.7 1.9c0 3.6-1.5 6.5-4.6 8.5s-7 3-11.7 3c-5.7 0-10.6-1.2-14.6-3.6l1.2-8.8 5.7.6-.2 4.7c1.1.5 2.3.9 3.6 1.1s2.6.3 3.9.3c2.4 0 4.5-.4 6.5-1.3 1.9-.9 2.9-2.2 2.9-4.1 0-1.8-.8-3.1-2.3-3.8s-3.5-1.3-5.8-1.7-4.6-.9-6.9-1.4c-2.3-.6-4.2-1.6-5.7-2.9-1.6-1.4-2.3-3.5-2.3-6.3 0-4.1 1.5-6.9 4.6-8.5s6.4-2.4 9.9-2.4c2.6 0 5 .3 7.2.9 2.2.6 4.3 1.4 6.1 2.4l.8 8.8-5.8.7-.8-5.7c-2.3-1-4.7-1.6-7.2-1.6-2.1 0-3.7.4-5.1 1.1-1.3.8-2 2-2 3.8 0 1.7.8 2.9 2.3 3.6 1.5.7 3.4 1.2 5.7 1.6 2.2.4 4.5.8 6.7 1.4 2.2.6 4.1 1.6 5.7 3 1.4 1.6 2.2 3.7 2.2 6.6zM197.6 73.2h-17.1v-5.5h3.8V51.9c0-3.7-.7-6.3-2.1-7.9-1.4-1.6-3.3-2.3-5.7-2.3-3.2 0-5.6 1.1-7.2 3.4s-2.4 4.6-2.5 6.9v15.6h6v5.5h-17.1v-5.5h3.8V51.9c0-3.8-.7-6.4-2.1-7.9-1.4-1.5-3.3-2.3-5.6-2.3-3.2 0-5.5 1.1-7.2 3.3-1.6 2.2-2.4 4.5-2.5 6.9v15.8h6.9v5.5h-20.2v-5.5h6V42.4h-6.1v-5.6h13.4v6.4c1.2-2.1 2.7-3.8 4.7-5.2 2-1.3 4.4-2 7.3-2s5.3.7 7.5 2.1c2.2 1.4 3.7 3.5 4.5 6.4 1.1-2.5 2.7-4.5 4.9-6.1s4.8-2.4 7.9-2.4c3.5 0 6.5 1.1 8.9 3.3s3.7 5.6 3.7 10.2v18.2h6.1v5.5zm42.5 0h-13.2V66c-1.2 2.2-2.8 4.1-4.9 5.6-2.1 1.6-4.8 2.4-8.3 2.4-4.8 0-8.7-1.6-11.6-4.9-2.9-3.2-4.3-7.7-4.3-13.3 0-5 1.3-9.6 4-13.7 2.6-4.1 6.9-6.2 12.8-6.2s9.8 2.2 12.3 6.5V22.7h-8.6v-5.6h15.8v50.6h6v5.5zm-13.3-16.8V52c-.1-3-1.2-5.5-3.2-7.3s-4.4-2.8-7.2-2.8c-3.6 0-6.3 1.3-8.2 3.9-1.9 2.6-2.8 5.8-2.8 9.6 0 4.1 1 7.3 3 9.5s4.5 3.3 7.4 3.3c3.2 0 5.8-1.3 7.8-3.8 2.1-2.6 3.1-5.3 3.2-8zm61.5 16.8H269v-5.5h6V51.9c0-3.7-.7-6.3-2.2-7.9-1.4-1.6-3.4-2.3-5.7-2.3-3.1 0-5.6 1-7.4 3s-2.8 4.4-2.9 7v15.9h6v5.5h-19.3v-5.5h6V42.4h-6.2v-5.6h13.6V43c2.6-4.6 6.8-6.9 12.7-6.9 3.6 0 6.7 1.1 9.2 3.3s3.7 5.6 3.7 10.2v18.2h6v5.4h-.2z" class="logo-text"></path></svg></a><button title="Open main menu" type="button" class="button action has-icon main-menu-toggle" aria-haspopup="menu" aria-label="Open main menu" aria-expanded="false"><span class="button-wrap"><span class="icon icon-menu "></span><span class="visually-hidden">Open main menu</span></span></button></div><div class="top-navigation-main"><nav class="main-nav" aria-label="Main menu"><ul class="main-menu nojs"><li class="top-level-entry-container "><button type="button" id="references-button" class="top-level-entry menu-toggle" aria-controls="references-menu" aria-expanded="false">References</button><a href="/zh-CN/docs/Web" class="top-level-entry">References</a><ul id="references-menu" class="submenu references hidden inline-submenu-lg" aria-labelledby="references-button"><li class="apis-link-container mobile-only "><a href="/zh-CN/docs/Web" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Overview / Web Technology</div><p class="submenu-item-description">Web technology reference for developers</p></div></a></li><li class="html-link-container "><a href="/zh-CN/docs/Web/HTML" class="submenu-item "><div class="submenu-icon html"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTML</div><p class="submenu-item-description">Structure of content on the web</p></div></a></li><li class="css-link-container "><a href="/zh-CN/docs/Web/CSS" class="submenu-item "><div class="submenu-icon css"></div><div class="submenu-content-container"><div class="submenu-item-heading">CSS</div><p class="submenu-item-description">Code used to describe document style</p></div></a></li><li class="javascript-link-container "><a href="/zh-CN/docs/Web/JavaScript" class="submenu-item "><div class="submenu-icon javascript"></div><div class="submenu-content-container"><div class="submenu-item-heading">JavaScript</div><p class="submenu-item-description">General-purpose scripting language</p></div></a></li><li class="http-link-container "><a href="/zh-CN/docs/Web/HTTP" class="submenu-item "><div class="submenu-icon http"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTTP</div><p class="submenu-item-description">Protocol for transmitting web resources</p></div></a></li><li class="apis-link-container "><a href="/zh-CN/docs/Web/API" class="submenu-item "><div class="submenu-icon apis"></div><div class="submenu-content-container"><div class="submenu-item-heading">Web APIs</div><p class="submenu-item-description">Interfaces for building web applications</p></div></a></li><li class="apis-link-container "><a href="/zh-CN/docs/Mozilla/Add-ons/WebExtensions" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Web Extensions</div><p class="submenu-item-description">Developing extensions for web browsers</p></div></a></li><li class=" "><a href="/zh-CN/docs/Web/Accessibility" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Accessibility</div><p class="submenu-item-description">Build web projects usable for all</p></div></a></li><li class="apis-link-container desktop-only "><a href="/zh-CN/docs/Web" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Web Technology</div><p class="submenu-item-description">Web technology reference for developers</p></div></a></li></ul></li><li class="top-level-entry-container active"><button type="button" id="learn-button" class="top-level-entry menu-toggle" aria-controls="learn-menu" aria-expanded="false">Learn</button><a href="/zh-CN/docs/Learn_web_development" class="top-level-entry">Learn</a><ul id="learn-menu" class="submenu learn hidden inline-submenu-lg" aria-labelledby="learn-button"><li class="apis-link-container mobile-only "><a href="/zh-CN/docs/Learn_web_development" class="submenu-item "><div class="submenu-icon learn"></div><div class="submenu-content-container"><div class="submenu-item-heading">Overview / MDN Learning Area</div><p class="submenu-item-description">Learn web development</p></div></a></li><li class="apis-link-container desktop-only "><a href="/zh-CN/docs/Learn_web_development" class="submenu-item "><div class="submenu-icon learn"></div><div class="submenu-content-container"><div class="submenu-item-heading">MDN Learning Area</div><p class="submenu-item-description">Learn web development</p></div></a></li><li class="html-link-container "><a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content" class="submenu-item "><div class="submenu-icon html"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTML</div><p class="submenu-item-description">Learn to structure web content with HTML</p></div></a></li><li class="css-link-container "><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics" class="submenu-item "><div class="submenu-icon css"></div><div class="submenu-content-container"><div class="submenu-item-heading">CSS</div><p class="submenu-item-description">Learn to style content using CSS</p></div></a></li><li class="javascript-link-container "><a href="/zh-CN/docs/Learn_web_development/Core/Scripting" class="submenu-item "><div class="submenu-icon javascript"></div><div class="submenu-content-container"><div class="submenu-item-heading">JavaScript</div><p class="submenu-item-description">Learn to run scripts in the browser</p></div></a></li><li class=" "><a href="/zh-CN/docs/Learn_web_development/Core/Accessibility" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Accessibility</div><p class="submenu-item-description">Learn to make the web accessible to all</p></div></a></li></ul></li><li class="top-level-entry-container "><button type="button" id="mdn-plus-button" class="top-level-entry menu-toggle" aria-controls="mdn-plus-menu" aria-expanded="false">Plus</button><a href="/zh-CN/plus" class="top-level-entry">Plus</a><ul id="mdn-plus-menu" class="submenu mdn-plus hidden inline-submenu-lg" aria-labelledby="mdn-plus-button"><li class=" "><a href="/zh-CN/plus" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Overview</div><p class="submenu-item-description">A customized MDN experience</p></div></a></li><li class=" "><a href="/zh-CN/plus/ai-help" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">AI Help</div><p class="submenu-item-description">Get real-time assistance and support</p></div></a></li><li class=" "><a href="/zh-CN/plus/updates" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Updates</div><p class="submenu-item-description">All browser compatibility updates at a glance</p></div></a></li><li class=" "><a href="/en-US/plus/docs/features/overview" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Documentation</div><p class="submenu-item-description">Learn how to use MDN Plus</p></div></a></li><li class=" "><a href="/en-US/plus/docs/faq" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">FAQ</div><p class="submenu-item-description">Frequently asked questions about MDN Plus</p></div></a></li></ul></li><li class="top-level-entry-container "><a class="top-level-entry menu-link" href="/en-US/curriculum/">Curriculum <sup class="new">New</sup></a></li><li class="top-level-entry-container "><a class="top-level-entry menu-link" href="/en-US/blog/">Blog</a></li><li class="top-level-entry-container "><button type="button" id="tools-button" class="top-level-entry menu-toggle" aria-controls="tools-menu" aria-expanded="false">Tools</button><ul id="tools-menu" class="submenu tools hidden inline-submenu-lg" aria-labelledby="tools-button"><li class=" "><a href="/zh-CN/play" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Playground</div><p class="submenu-item-description">Write, test and share your code</p></div></a></li><li class=" "><a href="/en-US/observatory" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTTP Observatory</div><p class="submenu-item-description">Scan a website for free</p></div></a></li><li class=" "><a href="/en-US/plus/ai-help" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">AI Help</div><p class="submenu-item-description">Get real-time assistance and support</p></div></a></li></ul></li></ul></nav><div class="header-search"><form action="/zh-CN/search" class="search-form search-widget" id="top-nav-search-form" role="search"><label id="top-nav-search-label" for="top-nav-search-input" class="visually-hidden">Search MDN</label><input aria-activedescendant="" aria-autocomplete="list" aria-controls="top-nav-search-menu" aria-expanded="false" aria-labelledby="top-nav-search-label" autoComplete="off" id="top-nav-search-input" role="combobox" type="search" class="search-input-field" name="q" placeholder="   " required="" value=""/><button type="button" class="button action has-icon clear-search-button"><span class="button-wrap"><span class="icon icon-cancel "></span><span class="visually-hidden">Clear search input</span></span></button><button type="submit" class="button action has-icon search-button"><span class="button-wrap"><span class="icon icon-search "></span><span class="visually-hidden">Search</span></span></button><div id="top-nav-search-menu" role="listbox" aria-labelledby="top-nav-search-label"></div></form></div><div class="theme-switcher-menu"><button type="button" class="button action has-icon theme-switcher-menu small" aria-haspopup="menu"><span class="button-wrap"><span class="icon icon-theme-os-default "></span>Theme</span></button></div><ul class="auth-container"><li><a href="/users/fxa/login/authenticate/?next=%2Fzh-CN%2Fdocs%2FLearn_web_development%2FExtensions%2FClient-side_APIs%2FDrawing_graphics" class="login-link" rel="nofollow">Log in</a></li><li><a href="/users/fxa/login/authenticate/?next=%2Fzh-CN%2Fdocs%2FLearn_web_development%2FExtensions%2FClient-side_APIs%2FDrawing_graphics" target="_self" rel="nofollow" class="button primary mdn-plus-subscribe-link"><span class="button-wrap">Sign up for free</span></a></li></ul></div></div></header><div class="article-actions-container"><div class="container"><button type="button" class="button action has-icon sidebar-button" aria-label="Expand sidebar" aria-expanded="false" aria-controls="sidebar-quicklinks"><span class="button-wrap"><span class="icon icon-sidebar "></span></span></button><nav class="breadcrumbs-container" aria-label="Breadcrumb"><ol typeof="BreadcrumbList" vocab="https://schema.org/" aria-label="breadcrumbs"><li property="itemListElement" typeof="ListItem"><a href="/zh-CN/docs/Learn_web_development" class="breadcrumb" property="item" typeof="WebPage"><span property="name">学习 Web 开发</span></a><meta property="position" content="1"/></li><li property="itemListElement" typeof="ListItem"><a href="/en-US/docs/Learn_web_development/Extensions" class="breadcrumb" property="item" typeof="WebPage"><span property="name">Extension modules</span></a><meta property="position" content="2"/></li><li property="itemListElement" typeof="ListItem"><a href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs" class="breadcrumb" property="item" typeof="WebPage"><span property="name">客户端 Web API</span></a><meta property="position" content="3"/></li><li property="itemListElement" typeof="ListItem"><a href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics" class="breadcrumb-current-page" property="item" typeof="WebPage"><span property="name">绘图</span></a><meta property="position" content="4"/></li></ol></nav><div class="article-actions"><button type="button" class="button action has-icon article-actions-toggle" aria-label="Article actions"><span class="button-wrap"><span class="icon icon-ellipses "></span><span class="article-actions-dialog-heading">Article Actions</span></span></button><ul class="article-actions-entries"><li class="article-actions-entry"><div class="languages-switcher-menu open-on-focus-within"><button id="languages-switcher-button" type="button" class="button action small has-icon languages-switcher-menu" aria-haspopup="menu"><span class="button-wrap"><span class="icon icon-language "></span>中文 (简体)</span></button><div class="hidden"><ul class="submenu language-menu " aria-labelledby="language-menu-button"><li class=" "><form class="submenu-item locale-redirect-setting"><div class="group"><label class="switch"><input type="checkbox" name="locale-redirect"/><span class="slider"></span><span class="label">Remember language</span></label><a href="https://github.com/orgs/mdn/discussions/739" rel="external noopener noreferrer" target="_blank" title="Enable this setting to automatically switch to this language when it&#x27;s available. (Click to learn more.)"><span class="icon icon-question-mark "></span></a></div></form></li><li class=" "><a data-locale="en-US" href="/en-US/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics" class="button submenu-item"><span>English (US)</span></a></li><li class=" "><a data-locale="de" href="/de/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics" class="button submenu-item"><span>Deutsch</span><span title="Diese Übersetzung ist Teil eines Experiments."><span class="icon icon-experimental "></span></span></a></li><li class=" "><a data-locale="fr" href="/fr/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics" class="button submenu-item"><span>Français</span></a></li><li class=" "><a data-locale="ja" href="/ja/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics" class="button submenu-item"><span>日本語</span></a></li></ul></div></div></li></ul></div></div></div></div><div class="container"><div class="notecard localized-content-note"><p><a href="/zh-CN/docs/MDN/Community/Contributing/Translated_content#活跃语言">此页面由社区从英文翻译而来。了解更多并加入 MDN Web Docs 社区。</a></p></div></div><div class="main-wrapper"><div class="sidebar-container"><aside id="sidebar-quicklinks" class="sidebar"><button type="button" class="button action backdrop" aria-label="Collapse sidebar"><span class="button-wrap"></span></button><nav aria-label="Related Topics" class="sidebar-inner"><header class="sidebar-actions"><section class="sidebar-filter-container"><div class="sidebar-filter "><label id="sidebar-filter-label" class="sidebar-filter-label" for="sidebar-filter-input"><span class="icon icon-filter"></span><span class="visually-hidden">Filter sidebar</span></label><input id="sidebar-filter-input" autoComplete="off" class="sidebar-filter-input-field false" type="text" placeholder="Filter" value=""/><button type="button" class="button action has-icon clear-sidebar-filter-button"><span class="button-wrap"><span class="icon icon-cancel "></span><span class="visually-hidden">Clear filter input</span></span></button></div></section></header><div class="sidebar-inner-nav"><div class="in-nav-toc"><div class="document-toc-container"><section class="document-toc"><header><h2 class="document-toc-heading">在本文中</h2></header><ul class="document-toc-list"><li class="document-toc-item "><a class="document-toc-link" href="#web_中的图形">Web 中的图形</a></li><li class="document-toc-item "><a class="document-toc-link" href="#主动学习:开始使用_canvas">主动学习:开始使用 &lt;canvas&gt;</a></li><li class="document-toc-item "><a class="document-toc-link" href="#2d_画布基础">2D 画布基础</a></li><li class="document-toc-item "><a class="document-toc-link" href="#循环和动画">循环和动画</a></li><li class="document-toc-item "><a class="document-toc-link" href="#webgl">WebGL</a></li><li class="document-toc-item "><a class="document-toc-link" href="#总结">总结</a></li><li class="document-toc-item "><a class="document-toc-link" href="#参见">参见</a></li><li class="document-toc-item "><a class="document-toc-link" href="#示例">示例</a></li></ul></section></div></div><div class="sidebar-body"><ol><li class="section"><a href="/zh-CN/docs/Learn_web_development/Getting_started">入门模块</a></li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Getting_started/Environment_setup">配置环境</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Getting_started/Environment_setup/Installing_software">安装基础软件</a></li><li><a href="/zh-CN/docs/Learn_web_development/Getting_started/Environment_setup/Browsing_the_web">网页、网站、网络服务器和搜索引擎的区别是什么?</a></li><li><a href="/en-US/docs/Learn_web_development/Getting_started/Environment_setup/Code_editors" class="only-in-en-us">Code editors</a></li><li><a href="/zh-CN/docs/Learn_web_development/Getting_started/Environment_setup/Dealing_with_files">处理文件</a></li><li><a href="/zh-CN/docs/Learn_web_development/Getting_started/Environment_setup/Command_line">命令行速成课</a></li></ol></details></li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Getting_started/Your_first_website">你的第一个网站</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Getting_started/Your_first_website/What_will_your_website_look_like">你的网站会是什么样子?</a></li><li><a href="/zh-CN/docs/Learn_web_development/Getting_started/Your_first_website/Creating_the_content">HTML 基础</a></li><li><a href="/zh-CN/docs/Learn_web_development/Getting_started/Your_first_website/Styling_the_content">CSS 基础</a></li><li><a href="/zh-CN/docs/Learn_web_development/Getting_started/Your_first_website/Adding_interactivity">JavaScript 基础</a></li><li><a href="/zh-CN/docs/Learn_web_development/Getting_started/Your_first_website/Publishing_your_website">发布你的网站</a></li></ol></details></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Getting_started/Web_standards" class="only-in-en-us">Web 标准</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Getting_started/Web_standards/How_the_web_works">万维网是如何工作的</a></li><li><a href="/zh-CN/docs/Learn_web_development/Getting_started/Web_standards/The_web_standards_model">Web 和 Web 标准</a></li><li><a href="/en-US/docs/Learn_web_development/Getting_started/Web_standards/How_browsers_load_websites" class="only-in-en-us">How browsers load websites</a></li></ol></details></li><li class="toggle"><details><summary><a href="/en-US/docs/Learn_web_development/Getting_started/Soft_skills" class="only-in-en-us">软性技能</a></summary><ol><li><a href="/en-US/docs/Learn_web_development/Getting_started/Soft_skills/Research_and_learning" class="only-in-en-us">Research and learning</a></li><li><a href="/en-US/docs/Learn_web_development/Getting_started/Soft_skills/Collaboration_and_teamwork" class="only-in-en-us">Collaboration and teamwork</a></li><li><a href="/en-US/docs/Learn_web_development/Getting_started/Soft_skills/Workflows_and_processes" class="only-in-en-us">Workflows and processes</a></li><li><a href="/en-US/docs/Learn_web_development/Getting_started/Soft_skills/Job_interviews" class="only-in-en-us">Succeeding in job interviews</a></li></ol></details></li><li class="section"><a href="/en-US/docs/Learn_web_development/Core" class="only-in-en-us">核心模块</a></li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content">使用 HTML 构建内容</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content/Basic_HTML_syntax">开始学习 HTML</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content/Webpage_metadata">“头”里有什么——HTML 元信息</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content/Headings_and_paragraphs">HTML 文本处理基础</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content/Emphasis_and_importance">强调与重要性</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/Lists" class="only-in-en-us">Lists</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content/Structuring_documents">文档与网站架构</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content/Advanced_text_features">文本格式进阶</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content/Creating_links">创建超链接</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content/Marking_up_a_letter">标记信件</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content/Structuring_a_page_of_content">构建网页内容</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content/HTML_images">HTML 中的图片</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content/HTML_video_and_audio">视频和音频内容</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content/Mozilla_splash_page">Mozilla 欢迎页面</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content/HTML_table_basics">HTML 表格基础</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content/Table_accessibility">HTML 表格进阶特性和无障碍</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content/Planet_data_table">作业:构建行星数据</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Structuring_content/HTML_forms" class="only-in-en-us">Forms and buttons in HTML</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content/Debugging_HTML">HTML 调试</a></li></ol></details></li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics">CSS 样式基础</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/What_is_CSS">CSS 如何运行</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Getting_started">让我们开始 CSS 的学习之旅</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Styling_a_bio_page">运用你的新知识</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Basic_selectors">CSS 选择器</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Attribute_selectors">属性选择器</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Pseudo_classes_and_elements">伪类和伪元素</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Combinators">关系选择器</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Box_model">盒模型</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Handling_conflicts">层叠、优先级与继承</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Values_and_units">CSS 值和单位</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Sizing">在 CSS 中调整大小</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Backgrounds_and_borders">背景与边框</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Overflow">溢出的内容</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Images_media_forms">图像、媒体和表单元素</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Tables">样式化表格</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Debugging_CSS">调试 CSS</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Fundamental_CSS_comprehension">基本的 CSS 理解</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Fancy_letterheaded_paper">创建精美的信纸</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Cool-looking_box">一个漂亮的盒子</a></li></ol></details></li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Core/Text_styling">CSS 文本样式</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Core/Text_styling/Fundamentals">基本文本和字体样式</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Text_styling/Styling_lists">为列表添加样式</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Text_styling/Styling_links">样式化链接</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Text_styling/Web_fonts">Web 字体</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Text_styling/Typesetting_a_homepage">作业:排版社区大学首页</a></li></ol></details></li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Core/CSS_layout">CSS 排版</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Core/CSS_layout/Introduction">介绍 CSS 布局</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/CSS_layout/Floats">浮动</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/CSS_layout/Positioning">定位</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/CSS_layout/Flexbox">弹性盒子</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/CSS_layout/Grids">网格</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/CSS_layout/Responsive_Design">响应式设计</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/CSS_layout/Media_queries">媒体查询入门指南</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/CSS_layout/Fundamental_Layout_Comprehension">作业:基本布局理解</a></li></ol></details></li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Core/Scripting">使用 JavaScript 动态编码</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/What_is_JavaScript">什么是 JavaScript?</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/A_first_splash">JavaScript 初体验</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/What_went_wrong">查找并解决 JavaScript 代码的错误</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/Variables">如何存储你需要的信息——变量</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/Math">JavaScript 中的基础数学 — 数字和操作符</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/Strings">文本处理——JavaScript 中的字符串</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/Useful_string_methods">有用的字符串方法</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/Arrays">数组</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/Silly_story_generator">笑话生成器</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/Conditionals">在代码中做决定——条件语句</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/Loops">循环吧,代码</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/Functions">函数——可复用的代码块</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/Build_your_own_function">创建你自己的函数</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/Return_values">函数返回值</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/Events">事件介绍</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/Event_bubbling">事件冒泡</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/Image_gallery">图片库</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/Object_basics">JavaScript 对象基础</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/DOM_scripting">DOM 脚本简介</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/Network_requests">从服务器获取数据</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/JSON">使用 JSON</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Scripting/Debugging_JavaScript">处理常见的 JavaScript 问题</a></li></ol></details></li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Core/Frameworks_libraries">JavaScript 架构和库</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Core/Frameworks_libraries/Introduction">客户端框架介绍</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Frameworks_libraries/Main_features">框架的主要特性</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Frameworks_libraries/React_getting_started">React 入门</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Frameworks_libraries/React_todo_list_beginning">开始我们的 React 待办清单</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Frameworks_libraries/React_components">组件化我们的 React App</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_interactivity_events_state" class="only-in-en-us">React interactivity: Events and state</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_interactivity_filtering_conditional_rendering" class="only-in-en-us">React interactivity: Editing, filtering, conditional rendering</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_accessibility" class="only-in-en-us">Accessibility in React</a></li><li><a href="/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_resources" class="only-in-en-us">React resources</a></li></ol></details></li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Core/Accessibility">无障碍</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Core/Accessibility/What_is_accessibility">什么是无障碍?</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Accessibility/Tooling">解决常见的无障碍问题</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Accessibility/HTML">HTML:无障碍的良好基础</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Accessibility/CSS_and_JavaScript">CSS 和 JavaScript 无障碍最佳实践</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Accessibility/WAI-ARIA_basics">WAI-ARIA 基础</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Accessibility/Multimedia">多媒体无障碍</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Accessibility/Mobile">移动端无障碍</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Accessibility/Accessibility_troubleshooting">测验:无障碍疑难解答</a></li></ol></details></li><li><a href="/en-US/docs/Learn_web_development/Core/Design_for_developers" class="only-in-en-us">为开发人员设计</a></li><li><a href="/zh-CN/docs/Learn_web_development/Core/Version_control">版本控制</a></li><li class="section"><a href="/en-US/docs/Learn_web_development/Extensions" class="only-in-en-us">扩展模块</a></li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects">Advanced JavaScript objects</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_prototypes">对象原型</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object-oriented_programming">面向对象编程基本概念</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Classes_in_JavaScript">JavaScript 中的类</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_building_practice">实践对象构造</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Adding_bouncing_balls_features">为“弹球”示例添加新功能</a></li></ol></details></li><li class="toggle"><details open=""><summary><a href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs">客户端 Web API</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Introduction">Web API 简介</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Video_and_audio_APIs">视频和音频 API</a></li><li><em><a href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics" aria-current="page">绘图</a></em></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Client-side_storage">客户端存储</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Third_party_APIs">第三方 API</a></li></ol></details></li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Extensions/Async_JS">异步 JavaScript</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Async_JS/Introducing">异步 JavaScript 简介</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Async_JS/Promises">如何使用 Promise</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Async_JS/Implementing_a_promise-based_API">如何实现基于 Promise 的 API</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Async_JS/Introducing_workers">worker 简介</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Async_JS/Sequencing_animations">序列动画</a></li></ol></details></li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Extensions/Forms">Web 表单</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Forms/Your_first_form">创建我的第一个表单</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Forms/How_to_structure_a_web_form">如何构建 HTML 表单</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Forms/Basic_native_form_controls">原生表单部件</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Forms/HTML5_input_types">HTML5 的输入(input)类型</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Forms/Other_form_controls">其他表单控件</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Forms/Styling_web_forms">样式化 Web 表单</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Forms/Advanced_form_styling">表单样式化进阶</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Forms/UI_pseudo-classes">UI 伪类</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Forms/Form_validation">表单数据校验</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Forms/Sending_and_retrieving_form_data">发送表单数据</a></li></ol></details></li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_tools">理解客户端工具</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_tools/Overview">客户端工具概述</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_tools/Package_management">软件包管理基础</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_tools/Introducing_complete_toolchain">介绍完整的工具链</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_tools/Deployment">部署我们的应用</a></li></ol></details></li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side">服务端网站</a></summary><ol><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/First_steps">服务端基础</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/First_steps/Introduction">服务端编程介绍</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/First_steps/Client-Server_overview">客户端服务端交互概述</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/First_steps/Web_frameworks">服务端 web 框架</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/First_steps/Website_security">站点安全</a></li></ol></details></li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django">Django web 框架(Python)</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Introduction">Django 介绍</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/development_environment">设置 Django 开发环境</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Tutorial_local_library_website">Django Tutorial: The Local Library website</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/skeleton_website">Django Tutorial Part 2: 创建网站的地基</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Models">Django Tutorial Part 3: 使用模型</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Admin_site">Django Tutorial Part 4: Django 管理员站点</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Home_page">Django 教程 5:主页构建</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Generic_views">Django 教程 6: 通用列表和详细信息视图</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Sessions">Django 教程 7: 会话框架</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Authentication">Django 教程 8:用户授权与许可</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Forms">Django 教程 9: 使用表单</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Testing">Django 教程 10: 测试 Django 网页应用</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Deployment">Django 教程 11:部署 Django 到生产环境</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/web_application_security">Django Web 应用安全</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/django_assessment_blog">评估:DIY Django 微博客</a></li></ol></details></li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs">Express web 框架(Node.js)</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Introduction">Express/Node 入门</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/development_environment">设置 Node 开发环境</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Tutorial_local_library_website">Express 教程:本地图书馆网站</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/skeleton_website">Express 教程 2:创建站点框架</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/mongoose">Express 教程 3:使用数据库 (Mongoose)</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/routes">Express 教程 4:路由和控制器</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Displaying_data">Express 教程 5: 呈现图书馆数据</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/forms">Express 教程 6: 使用表单</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/deployment">Express 教程 7:部署到生产环境</a></li></ol></details></li></ol></details></li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Extensions/Performance">Web 性能</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Performance/why_web_performance">Web 性能的重要性</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Performance/What_is_web_performance">什么是 web 性能?</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Performance/Perceived_performance">感知性能</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Performance/Measuring_performance">测量性能</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Performance/Multimedia">多媒体:图片</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Performance/video">多媒体:视频</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Performance/JavaScript">JavaScript 性能优化</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Performance/HTML">HTML 性能优化</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Performance/CSS">CSS 性能优化</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Performance/business_case_for_performance">web 性能的商业案例</a></li></ol></details></li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Extensions/Testing">测试</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Testing/Introduction">跨浏览器测试介绍</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Testing/Testing_strategies">测试的策略</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Testing/HTML_and_CSS">处理常见的 HTML 和 CSS 问题</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Testing/Feature_detection">实现特性检测</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Testing/Automated_testing">自动化测试简介</a></li><li><a href="/zh-CN/docs/Learn_web_development/Extensions/Testing/Your_own_automation_environment">搭建自己的自动化测试环境</a></li></ol></details></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Transform_animate" class="only-in-en-us">CSS 变换与动画化</a></li><li><a href="/en-US/docs/Learn_web_development/Extensions/Security_privacy" class="only-in-en-us">安全与隐私</a></li><li class="section">更多资源</li><li class="toggle"><details><summary><a href="/zh-CN/docs/Learn_web_development/Howto">如何解决常见问题</a></summary><ol><li><a href="/zh-CN/docs/Learn_web_development/Howto/Solve_HTML_problems">使用 HTML 解决常见问题</a></li><li><a href="/zh-CN/docs/Learn_web_development/Howto/Solve_CSS_problems">解决常见的 CSS 问题</a></li><li><a href="/zh-CN/docs/Learn_web_development/Howto/Solve_JavaScript_problems">解决 JavaSctript 代码的常见问题</a></li><li><a href="/zh-CN/docs/Learn_web_development/Howto/Web_mechanics">Web 机制</a></li><li><a href="/zh-CN/docs/Learn_web_development/Howto/Tools_and_setup">工具和安装</a></li><li><a href="/zh-CN/docs/Learn_web_development/Howto/Design_and_accessibility">设计与无障碍</a></li></ol></details></li><li><a href="/en-US/docs/Learn_web_development/About" class="only-in-en-us">关于</a></li><li><a href="/en-US/docs/Learn_web_development/Educators" class="only-in-en-us">面向教育工作者的资源</a></li><li><a href="/en-US/docs/Learn_web_development/Changelog" class="only-in-en-us">更新日志</a></li></ol></div></div><section class="place side"></section></nav></aside><div class="toc-container"><aside class="toc"><nav><div class="document-toc-container"><section class="document-toc"><header><h2 class="document-toc-heading">在本文中</h2></header><ul class="document-toc-list"><li class="document-toc-item "><a class="document-toc-link" href="#web_中的图形">Web 中的图形</a></li><li class="document-toc-item "><a class="document-toc-link" href="#主动学习:开始使用_canvas">主动学习:开始使用 &lt;canvas&gt;</a></li><li class="document-toc-item "><a class="document-toc-link" href="#2d_画布基础">2D 画布基础</a></li><li class="document-toc-item "><a class="document-toc-link" href="#循环和动画">循环和动画</a></li><li class="document-toc-item "><a class="document-toc-link" href="#webgl">WebGL</a></li><li class="document-toc-item "><a class="document-toc-link" href="#总结">总结</a></li><li class="document-toc-item "><a class="document-toc-link" href="#参见">参见</a></li><li class="document-toc-item "><a class="document-toc-link" href="#示例">示例</a></li></ul></section></div></nav></aside><section class="place side"></section></div></div><main id="content" class="main-content "><article class="main-page-content" lang="zh-CN"><header><h1>绘图</h1></header><div class="section-content"><ul class="prev-next"><li><a class="button secondary" href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Video_and_audio_APIs"><span class="button-wrap"> 上一页 </span></a></li><li><a class="button secondary" href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs"><span class="button-wrap"> 概述:客户端 Web API</span></a></li><li><a class="button secondary" href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Client-side_storage"><span class="button-wrap"> 下一页 </span></a></li></ul> <p>浏览器包含一些非常强大的图形编程工具,从可缩放矢量图形(Scalable Vector Graphics,简称 <a href="/zh-CN/docs/Web/SVG">SVG</a>)语言到用于在 HTML <a href="/zh-CN/docs/Web/HTML/Element/canvas"><code>&lt;canvas&gt;</code></a> 元素上绘制图形的 API(参见 <a href="/zh-CN/docs/Web/API/Canvas_API">Canvas API</a> 和 <a href="/zh-CN/docs/Web/API/WebGL_API">WebGL</a>)。本文将介绍 canvas,并提供更多的学习资源。</p> <figure class="table-container"><table> <tbody> <tr> <th scope="row">前提:</th> <td> JavaScript 基础(见 <a href="/zh-CN/docs/Learn_web_development/Core/Scripting">JavaScript 第一步</a>、<a href="/zh-CN/docs/Learn_web_development/Core/Scripting">创建 JavaScript 代码块</a>、<a href="/zh-CN/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects">JavaScript 对象</a>)、<a href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Introduction">客户端 API 基础知识</a>。 </td> </tr> <tr> <th scope="row">目标:</th> <td> 学习 JavaScript 在 <code>&lt;canvas&gt;</code> 元素中绘图的基础知识。 </td> </tr> </tbody> </table></figure></div><section aria-labelledby="web_中的图形"><h2 id="web_中的图形"><a href="#web_中的图形">Web 中的图形</a></h2><div class="section-content"><p>我们来讨论 HTML 的<a href="/zh-CN/docs/Learn_web_development/Core/Structuring_content">多媒体和嵌入</a>模块,早先的网页只有单调的文字,后来才引入了图像,起初是通过 <a href="/zh-CN/docs/Web/HTML/Element/img"><code>&lt;img&gt;</code></a> 元素的方式,后来出现了类似于 <a href="/zh-CN/docs/Web/CSS/background-image"><code>background-image</code></a> 的 CSS 属性和 <a href="/zh-CN/docs/Web/SVG">SVG</a> 图像等方式。</p> <p>然而,这还不够好。当你能够使用 <a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics">CSS</a> 和 <a href="/zh-CN/docs/Learn_web_development/Core/Scripting">JavaScript</a> 让 SVG 矢量图(因为其由标记表示)动起来时,位图却依然没有相应的支持。同时 SVG 动画的可用工具也少得可怜。Web 仍然无法高效地创建动画、游戏、3D 场景,而其他需求则通常由底层语言(如 C++ 或 Java)来应对。</p> <p>当浏览器于 2004 年开始支持 HTML 画布元素 <a href="/zh-CN/docs/Web/HTML/Element/canvas"><code>&lt;canvas&gt;</code></a> 和相关的 <a href="/zh-CN/docs/Web/API/Canvas_API">Canvas API</a> 时,形势开始改善。下面你会看到,canvas 提供了许多用于创建 2D 动画、游戏、数据可视化和其他应用类型的有用工具,特别是当捆绑了由 Web 平台提供的一些其他的 API 时。</p> <p>下面的例子显示的是一个基于 canvas 的简单的 2D 弹跳球动画,前面我们在<a href="/zh-CN/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_building_practice">介绍 JavaScript 对象</a>模块中见到过。</p> <iframe width="100%" height="500" src="https://mdn.github.io/learning-area/javascript/oojs/bouncing-balls/index-finished.html" loading="lazy"></iframe> <p>大约在 2006—2007 年,Mozilla 开始测试 3D 画布实现。后来演化为 <a href="/zh-CN/docs/Web/API/WebGL_API">WebGL</a>,它获得了各大浏览器厂商的认可,于是大约在 2009—2010 年间得到了标准化。WebGL 可以让你在 Web 浏览器中生成真正的 3D 图形。下面的例子显示了一个简单的旋转的 WebGL 立方体:</p> <iframe width="100%" height="500" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/threejs-cube/index.html" loading="lazy"></iframe> <p>由于原生的 WebGL 代码非常复杂,本文主要针对 2D 画布。然而,你也可以通过<a href="/zh-CN/docs/Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL">初识 WebGL</a> 找到 WebGL 原生代码的教程,来学习如何更容易地使用 WebGL 库来创建一个 3D 场景。</p></div></section><section aria-labelledby="主动学习:开始使用_canvas"><h2 id="主动学习:开始使用_canvas"><a href="#主动学习:开始使用_canvas">主动学习:开始使用 &lt;canvas&gt;</a></h2><div class="section-content"><p>要在网页中创建 2D <em>或</em> 3D 场景,必须从 HTML <a href="/zh-CN/docs/Web/HTML/Element/canvas"><code>&lt;canvas&gt;</code></a> 元素开始。该元素用于定义页面中的绘图区域。这与在页面中包含元素一样简单:</p> <div class="code-example"><div class="example-header"><span class="language-name">html</span></div><pre class="brush: html notranslate"><code>&lt;canvas width="320" height="240"&gt;&lt;/canvas&gt; </code></pre></div> <p>网页中会生成一块 320 × 240 像素的画布。</p> <p>在 <code>&lt;canvas&gt;</code> 标签内,你可以放置一些回退内容。这会向不支持画布的浏览器或屏幕阅读器的用户描述画布内容。</p> <div class="code-example"><div class="example-header"><span class="language-name">html</span></div><pre class="brush: html notranslate"><code>&lt;canvas width="320" height="240"&gt; &lt;p&gt;为无法查看画布的用户提供的画布描述。&lt;/p&gt; &lt;/canvas&gt; </code></pre></div> <p>回退内容应为画布内容提供有用的替代内容。比如,如果你要渲染不断更新的股价曲线图,回退内容可以是最新股价图的静态图像,并带有替代文本(以文本的形式说明价格)或指向各个股票页面的链接列表。</p> <div class="notecard note"> <p><strong>备注:</strong> 屏幕阅读器无法获取画布内容。需要在画布元素的 <a href="/zh-CN/docs/Web/Accessibility/ARIA/Attributes/aria-label"><code>aria-label</code></a> 属性填写描述性文本,或者在起始和结束的 <code>&lt;canvas&gt;</code> 标签里嵌入回退内容。画布内容不是 DOM 的一部分,但嵌入的回退内容是 DOM 的一部分。</p> </div></div></section><section aria-labelledby="创建画布并确定尺寸"><h3 id="创建画布并确定尺寸"><a href="#创建画布并确定尺寸">创建画布并确定尺寸</a></h3><div class="section-content"><p>让我们开始吧:创建画布,准备尝试绘制图形。</p> <ol> <li> <p>首先在本地拷贝一份 <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/0_canvas_start" class="external" target="_blank">0_canvas_start</a> 文件夹。包含三个文件:</p> <ul> <li>“index.html”</li> <li>“script.js”</li> <li>“style.css”</li> </ul> </li> <li> <p>打开“index.html”,在 <a href="/zh-CN/docs/Web/HTML/Element/body"><code>&lt;body&gt;</code></a> 的起始标签下面填加以下代码。</p> <div class="code-example"><div class="example-header"><span class="language-name">html</span></div><pre class="brush: html notranslate"><code>&lt;canvas class="myCanvas"&gt; &lt;p&gt;添加恰当的回退信息。&lt;/p&gt; &lt;/canvas&gt; </code></pre></div> <p>我们为 <code>&lt;canvas&gt;</code> 元素添加了一个 <code>class</code>,使得在网页中选择多个画布时会容易些。这里我们移除了 <code>width</code> 和 <code>height</code> 属性(你可以随时添上,但是我们会在下方用 JavaScript 把它们添加回来)。不明确指定宽高时,画布默认尺寸为 300 × 150 像素。</p> </li> <li> <p>现在,打开“script.js”并添加以下 JavaScript 代码:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const canvas = document.querySelector(".myCanvas"); const width = (canvas.width = window.innerWidth); const height = (canvas.height = window.innerHeight); </code></pre></div> <p>这里我们用 <code>canvas</code> 常量来存储画布的引用。第二行中我们将 <a href="/zh-CN/docs/Web/API/Window/innerWidth"><code>Window.innerWidth</code></a>(可视区域宽度)赋值给一个新常量 <code>width</code> 和画布的 <code>width</code> 属性。第三行中,我们将 <a href="/zh-CN/docs/Web/API/Window/innerHeight"><code>Window.innerHeight</code></a>(可视区域高度)赋值给一个新常量 <code>height</code> 和画布的 <code>height</code> 属性。然后我们就得到了一个充满浏览器窗口的画布。</p> <p>你还可以看到我们使用了多个等号来进行链式赋值,这在 JavaScript 中是允许的,很适合为多个变量同时赋一个相同的值。我们想要使用 width/height 变量来更方便地访问画布的宽和高,因为它们是后面很有用的值(例如,你想在画布宽度的一半处绘制某个东西)。</p> </li> </ol> <div class="notecard note"> <p><strong>备注:</strong> 如上文所讲,一般情况下图片的尺寸可以通过 HTML 属性或 DOM 属性来设定。你也可以使用 CSS,但问题是大小调整是在画布渲染后进行的,就会与其他图像一样(其实渲染好的画布就是一幅图片),所显示的内容将变得像素化或扭曲变形。</p> </div></div></section><section aria-labelledby="获取画布上下文并完成设置"><h3 id="获取画布上下文并完成设置"><a href="#获取画布上下文并完成设置">获取画布上下文并完成设置</a></h3><div class="section-content"><p>画布模板设置还有最后一步。我们需要获得一个对绘画区域的特殊的引用(称为“上下文”)用来在画布上绘图。可通过 <a href="/zh-CN/docs/Web/API/HTMLCanvasElement/getContext"><code>HTMLCanvasElement.getContext()</code></a> 方法获得基础的绘画功能,需要提供一个字符串参数来表示所需上下文的类型。</p> <p>这里我们需要一个 2d 画布,在“script.js”中添加以下 JavaScript 代码:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const ctx = canvas.getContext("2d"); </code></pre></div> <div class="notecard note"> <p><strong>备注:</strong> 可选上下文还包括 WebGL(<code>webgl</code>)、WebGL 2(<code>webgl2</code>)等等,但本文暂不涉及。</p> </div> <p>好啦,我们的画布现在已经准备好并可以绘制了!<code>ctx</code> 变量包含一个 <a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D"><code>CanvasRenderingContext2D</code></a> 对象,画布上所有绘画操作都会涉及到这个对象。</p> <p>在继续之前,我们先做最后一件事。我们将画布背景涂成黑色,让你初步体验 canvas API。在 JavaScript 底部添加以下几行:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillStyle = "rgb(0, 0, 0)"; ctx.fillRect(0, 0, width, height); </code></pre></div> <p>这里我们使用画布的 <a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/fillStyle" title="fillStyle"><code>fillStyle</code></a> 属性(和 CSS 属性<a href="/zh-CN/docs/Learn_web_development/Core/Styling_basics/Values_and_units#颜色">色值</a>一致)设置填充色,然后使用 <a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/fillRect" title="fillRect"><code>fillRect</code></a> 方法绘制一个覆盖整个区域的矩形(前两个参数是矩形左上顶点的坐标,后两个参数是矩形的长宽,现在你知道 <code>width</code> 和 <code>height</code> 的作用了吧)。</p> <p>好的,模板已经就位,我们要开始了。</p></div></section><section aria-labelledby="2d_画布基础"><h2 id="2d_画布基础"><a href="#2d_画布基础">2D 画布基础</a></h2><div class="section-content"><p>如上文所讲,所有绘画操作都离不开 <a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D"><code>CanvasRenderingContext2D</code></a> 对象(这里叫做 <code>ctx</code>)。许多操作都需要提供坐标来指示绘图的确切位置。画布左上角的坐标是 (0, 0),横坐标(x)轴向右延伸,纵坐标(y)轴向下延伸。</p> <p><img src="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics/canvas_default_grid.png" alt="小方块网格图,中间为钢蓝色方块。画布的左上角是画布 x 轴和 y 轴的原点(0,0)。水平(x)轴从左到右表示宽度,垂直(y)轴从上到下表示高度。蓝色方块的左上角被标注为距离 y 轴 x 个单位,距离 x 轴 y 个单位。" width="220" height="220" loading="lazy"></p> <p>绘图操作可基于矩形原语实现,也可通过追踪一个特定路径后填充颜色实现。下面分别讲解。</p></div></section><section aria-labelledby="简单矩形"><h3 id="简单矩形"><a href="#简单矩形">简单矩形</a></h3><div class="section-content"><p>让我们从简单矩形开始。</p> <ol> <li> <p>首先,复制一份刚才创建的画布模板(如果你没有按上述步骤进行,请在本地创建 <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template" class="external" target="_blank">1_canvas_template</a> 目录的副本)。</p> </li> <li> <p>然后在 JavaScript 末尾添加以下几行:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillStyle = "rgb(255, 0, 0)"; ctx.fillRect(50, 50, 100, 150); </code></pre></div> <p>保存并刷新,画布上将出现一个红色的矩形。其左边和顶边与画布边缘距离均为 50 像素(由前两个参数指定),宽 100 像素、高 150 像素(由后两个参数指定)。</p> </li> <li> <p>然后再添加一个绿色矩形。在 JavaScript 末尾添加以下行:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillStyle = "rgb(0, 255, 0)"; ctx.fillRect(75, 75, 100, 100); </code></pre></div> <p>保存并刷新,新的矩形就会出现。这里引出了一个新问题:绘制矩形、线等操作按出现的顺序依次进行。就像粉刷墙面时,两层重叠时新层总会覆盖旧层。这一点是无法改变的,因此在绘制图形时一定要慎重考虑顺序问题。</p> </li> <li> <p>你还可以通过指定半透明的颜色来绘制半透明的图形,比如使用 <code>rgba()</code>。“α 通道”定义了颜色的透明度。值越高透明度越高,底层的内容就越清晰。在代码中添加以下行:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillStyle = "rgba(255, 0, 255, 0.75)"; ctx.fillRect(25, 100, 175, 50); </code></pre></div> </li> <li> <p>现在你可以自己尝试绘制一些矩形了,玩得开心!</p> </li> </ol></div></section><section aria-labelledby="描边和线宽"><h3 id="描边和线宽"><a href="#描边和线宽">描边和线宽</a></h3><div class="section-content"><p>目前我们绘制的矩形都是填充颜色的,我们也可以绘制仅包含外部框线(图形设计中称为<strong>描边</strong>)的矩形。你可以使用 <a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/strokeStyle" title="strokeStyle"><code>strokeStyle</code></a> 属性来设置描边颜色,使用 <a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/strokeRect" title="strokeRect"><code>strokeRect</code></a> 来绘制一个矩形的轮廓。</p> <ol> <li> <p>在上文的 JavaScript 代码的末尾添加以下行:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.strokeStyle = "rgb(255, 255, 255)"; ctx.strokeRect(25, 25, 175, 200); </code></pre></div> </li> <li> <p>默认的描边宽度是 1 像素,可以通过调整 <a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/lineWidth" title="lineWidth"><code>lineWidth</code></a> 属性(接受一个表示描边宽度像素值的数字)的值来修改。在上文两行后添加以下行:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.lineWidth = 5; </code></pre></div> </li> </ol> <p>现在可以看到白色的外边框变得更粗了。就这么简单,示例看上去像这样:</p> <iframe width="100%" height="250" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/2_canvas_rectangles/index.html" loading="lazy"></iframe> <div class="notecard note"> <p><strong>备注:</strong> 完整代码请访问 GitHub:<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/2_canvas_rectangles" class="external" target="_blank">2_canvas_rectangles</a>。</p> </div></div></section><section aria-labelledby="绘制路径"><h3 id="绘制路径"><a href="#绘制路径">绘制路径</a></h3><div class="section-content"><p>可以通过绘制路径来绘制比矩形更复杂的图形。路径中至少要包含笔运行精确路径的代码以确定图形的形状。画布提供了许多函数用来绘制直线、圆、贝塞尔曲线,等等。</p> <p>让我们从复制画布模板(<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template" class="external" target="_blank">1_canvas_template</a>)开始这一部分,以便在其中绘制新的示例。</p> <p>一些通用的方法和属性将贯穿以下全部内容:</p> <ul> <li><a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/beginPath" title="beginPath()"><code>beginPath()</code></a>:在笔当前所在位置开始绘制一条路径。在新的画布中,笔起始位置为 (0, 0)。</li> <li><a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/moveTo" title="moveTo()"><code>moveTo()</code></a>:将笔移动至另一个坐标点,不记录、不留痕迹,只将笔“跳”至新位置。</li> <li><a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/fill" title="fill()"><code>fill()</code></a>:通过为当前所绘制路径的区域填充颜色来绘制一个新的填充形状。</li> <li><a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/stroke" title="stroke()"><code>stroke()</code></a>:通过为当前绘制路径的区域描边,来绘制一个只有边框的形状。</li> <li>路径也可和矩形一样使用 <code>lineWidth</code> 和 <code>fillStyle</code> / <code>strokeStyle</code> 等特性。</li> </ul> <p>以下是一个典型的简单路径绘制操作:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillStyle = "rgb(255, 0, 0)"; ctx.beginPath(); ctx.moveTo(50, 50); // 绘制路径 ctx.fill(); </code></pre></div> <h4 id="画线">画线</h4> <p>我们来在画布上绘制一个等边三角形。</p> <ol> <li> <p>首先,在代码底部添加下面的辅助函数。它可以将角度换算为弧度,在为 JavaScript 提供角度值时非常实用,JavaScript 基本上只接受弧度值,而人类更习惯用角度值。</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function degToRad(degrees) { return (degrees * Math.PI) / 180; } </code></pre></div> </li> <li> <p>然后,在刚才复制好的文件中添加下面的内容,以开始路径的绘制。此处为我们为三角形设置了颜色,准备绘制,然后将笔移动至 (50, 50)(没有绘制任何内容)。然后准备在新的坐标开始绘制三角形。</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillStyle = "rgb(255, 0, 0)"; ctx.beginPath(); ctx.moveTo(50, 50); </code></pre></div> </li> <li> <p>接下来在脚本中添加以下代码:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.lineTo(150, 50); const triHeight = 50 * Math.tan(degToRad(60)); ctx.lineTo(100, 50 + triHeight); ctx.lineTo(50, 50); ctx.fill(); </code></pre></div> <p>我们来逐行解释:</p> <p>首先绘制一条直线,终点坐标为 (150, 50)。此时路径沿 x 轴向右行走 100 像素。</p> <p>然后利用三角函数来计算等边三角形的高。这里我们要绘制的三角形是朝下的。等边三角形每个角均为 60°,为计算高的值,我们可以将三角形从正中心分割为两个直角三角形,每个直角三角形的三个角分别为 90°、60°、30°。对于边:</p> <ul> <li>最长的边称为<strong>斜边</strong>。</li> <li>紧挨 60° 角的边称为<strong>临边</strong>,显然地,它的长度是刚才绘制的线的一半,即 50 像素。</li> <li>60° 角对面的边称为<strong>对边</strong>,即三角形的高,需要计算得到。</li> </ul> <p><img src="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics/trigonometry.png" alt="一个指向下方的等边三角形,标有角和边。顶部的水平线标注“临边”。从相邻线中间开始的一条垂直虚线标注“对边”,它将三角形分割成两个相等的直角三角形。三角形的右边被标为“斜边”,因为它是标有“对边”的直线所构成的直角三角形的斜边。等边三角形三边的长度相等,同时斜边是直角三角形最长的边。" width="200" height="166" loading="lazy"></p> <p>通过基本三角函数可得:临边长度乘以角的正切等于对边长度。于是可得三角形的高为 <code>50 * Math.tan(degToRad(60))</code>。由于 <a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/tan"><code>Math.tan()</code></a> 接受数值的单位为弧度,于是我们用刚才的 <code>degToRad()</code> 函数将 60° 换算为弧度。</p> </li> <li> <p>有了三角形的高,我们来绘制另一条线,终点坐标为 <code>(100, 50+triHeight)</code>。X 坐标值很简单,应在刚才绘制的水平线两顶点正中间位置。Y 值应为 50 加上三角形的高,因为高即三角形底边到顶点的距离。</p> </li> <li> <p>下一条线的终点坐标为绘制整个三角形的起点坐标。</p> </li> <li> <p>最后,运行 <code>ctx.fill()</code> 来终止路径,并为图形填充颜色。</p> </li> </ol> <h4 id="画圆">画圆</h4> <p>下面来看可在画布中绘制圆的方法——<a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/arc" title="arc()"><code>arc()</code></a>,通过连续的点来绘制整个圆或者弧(arc,即局部的圆)。</p> <ol> <li> <p>在代码底部添加以下几行,以向画布中添加一条弧。</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillStyle = "rgb(0, 0, 255)"; ctx.beginPath(); ctx.arc(150, 106, 50, degToRad(0), degToRad(360), false); ctx.fill(); </code></pre></div> <p><code>arc()</code> 有六个参数。前两个指定圆心的位置坐标,第三个是圆的半径,第四、五个是绘制弧的起、止角度(给定 0° 和 360° 便能绘制一个完整的圆),第六个参数指定圆是应该逆时针绘制还是顺时针绘制(<code>false</code> 是顺时针)。</p> <div class="notecard note"> <p><strong>备注:</strong> 0° 设定为水平向右。</p> </div> </li> <li> <p>我们再来画一条弧:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillStyle = "yellow"; ctx.beginPath(); ctx.arc(200, 106, 50, degToRad(-45), degToRad(45), true); ctx.lineTo(200, 106); ctx.fill(); </code></pre></div> <p>模式基本一样,但有两点不同:</p> <ul> <li>将 <code>arc()</code> 的最后一个参数设置为 <code>true</code>,意味着弧将逆时针绘制,也就意味着即使起、止角度分别设置为 -45°、45°,我们还是得到了区域外的一条 270° 的弧。如果把 <code>true</code> 改为 <code>false</code> 重新运行,将得到 90° 的弧。</li> <li>在调用 <code>fill()</code> 前,我们绘制了一条终点为圆心的直线。然后我们就渲染出一个惟妙惟肖的吃豆人模型。如果删除这条线(试试呗)再重新运行代码,你只能得到一个起止点间被砍掉一块的圆。这向我们展示了画布的另一个重要事项:如果要填充一个未完成(也就是没有首尾相接)的路径,浏览器将在起、止点件绘制一条直线,然后直接填充。</li> </ul> </li> </ol> <p>示例现在应该跟下图一致:</p> <iframe width="100%" height="200" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/3_canvas_paths/index.html" loading="lazy"></iframe> <div class="notecard note"> <p><strong>备注:</strong> 完整代码可到 GitHub 下载:<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/3_canvas_paths" class="external" target="_blank">3_canvas_paths</a>。</p> </div> <div class="notecard note"> <p><strong>备注:</strong> 请访问我们的<a href="/zh-CN/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes">用画布绘图</a>入门课程来学习更多高级的路径绘制特征,比如贝叶斯曲线。</p> </div></div></section><section aria-labelledby="文本"><h3 id="文本"><a href="#文本">文本</a></h3><div class="section-content"><p>画布可用于绘制文本。我们简要学习一下。首先再次下载一份新的画布模板(<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template" class="external" target="_blank">1_canvas_template</a>),我们用它来绘制新的示例。</p> <p>以下两个函数用于绘制文本:</p> <ul> <li><a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/fillText" title="fillText()"><code>fillText()</code></a>:绘制有填充色的文本。</li> <li><a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/strokeText" title="strokeText()"><code>strokeText()</code></a>:绘制文本外边框(描边)。</li> </ul> <p>这两个函数有三个基本的参数:需要绘制的文字字符串以及绘制起始点的 X、Y 坐标。以<strong>文本框</strong>(即包裹绘制文本的框体)的<strong>左下角</strong>作为起始点。这可能会让你感到困惑,因为其他绘制操作都是以左上角作起始点,留意这一点。</p> <p>还有一系列帮助控制文本渲染的属性:比如用于指定字体族、字号的 <a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/font" title="font"><code>font</code></a>,它的值和语法与 CSS 的 <a href="/zh-CN/docs/Web/CSS/font"><code>font</code></a> 属性一致。</p> <p>屏幕阅读器无法获取画布内容。DOM 无法获取在画布绘制的文本,但我们仍要支持 DOM 获取文本。在本例中,我们将文本作为值赋给 <code>aria-label</code>。</p> <p>在 JavaScript 代码底部添加以下内容:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.strokeStyle = "white"; ctx.lineWidth = 1; ctx.font = "36px arial"; ctx.strokeText("Canvas text", 50, 50); ctx.fillStyle = "red"; ctx.font = "48px georgia"; ctx.fillText("Canvas text", 50, 150); canvas.setAttribute("aria-label", "Canvas text"); </code></pre></div> <p>将绘制两行文字,一行描边文字一行填充颜色的文字。示例最终如下所示:</p> <iframe width="100%" height="180" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/4_canvas_text/index.html" loading="lazy"></iframe> <div class="notecard note"> <p><strong>备注:</strong> 完整代码可到 GitHub 下载:<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/4_canvas_text" class="external" target="_blank">4_canvas_text</a>。</p> </div> <p>可以自己尝试一下。访问<a href="/zh-CN/docs/Web/API/Canvas_API/Tutorial/Drawing_text">绘制文本</a>获得关于画布文本选项的更多信息。</p></div></section><section aria-labelledby="在画布上绘制图片"><h3 id="在画布上绘制图片"><a href="#在画布上绘制图片">在画布上绘制图片</a></h3><div class="section-content"><p>可在画布上渲染外部图片,简单图片文件、视频帧、其他画布内容都可以。这里我们只考虑简单图片文件的情况:</p> <ol> <li> <p>同上,再次拷贝画布模板(<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template" class="external" target="_blank">1_canvas_template</a>)以绘制新的示例。</p> <p><a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/drawImage" title="drawImage()"><code>drawImage()</code></a> 方法可将图片绘制在画布上。最简单的版本需要三个参数:需要渲染的图片、图片左上角的 X、Y 坐标。</p> </li> <li> <p>首先将图片源嵌入画布中。将以下几行添加到 JavaScript 的底部:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const image = new Image(); image.src = "firefox.png"; </code></pre></div> <p>这里使用 <a href="/zh-CN/docs/Web/API/HTMLImageElement/Image" title="Image()"><code>Image()</code></a> 构造函数创建了一个新的 <a href="/zh-CN/docs/Web/API/HTMLImageElement"><code>HTMLImageElement</code></a> 对象。返回对象的类型与非空 <a href="/zh-CN/docs/Web/HTML/Element/img"><code>&lt;img&gt;</code></a> 元素的引用是一致的。然后将它的 <a href="/zh-CN/docs/Web/HTML/Element/img#src"><code>src</code></a> 属性设置为 Firefox 的图标。此时浏览器将开始载入这张图片。</p> </li> <li> <p>这次我们尝试用 <code>drawImage()</code> 函数来嵌入图片,应确保图片先载入完毕,否则运行会出错。可以通过 <code>load</code> 事件处理器来达成,该函数只在图片调用完毕后才会调用。在上文代码末尾添加以下内容:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>image.addEventListener("load", () =&gt; ctx.drawImage(image, 20, 20)); </code></pre></div> <p>保存刷新,可以看到图片成功嵌入画布中。</p> </li> <li> <p>还有更多方式。如果仅需要显示图片的某一部分,或者需要改变尺寸,该怎么做呢?复杂版本的 <code>drawImage()</code> 可解决这两个问题。请将 <code>ctx.drawImage()</code> 这一行更新为:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.drawImage(image, 20, 20, 185, 175, 50, 50, 185, 175); </code></pre></div> <ul> <li>第一个参数不变,为图片引用。</li> <li>参数 2、3 表示裁切部分左上顶点的坐标,参考原点为原图片本身左上角的坐标。原图片在该坐标左、上的部分均不会绘制出来。</li> <li>参数 4、5 表示我们希望从加载的原始图像中裁切出的部分的宽和高。</li> <li>参数 6、7 表示裁切部分左上顶点在画布中的位置坐标,参考原点为画布左上顶点。</li> <li>参数 8、9 表示裁切部分在画布中绘制的宽和高。本例中绘制时与裁切时面积相同,但你也可以指定不同的值来调整其大小。</li> </ul> </li> <li> <p>当图片完全更新后,<a href="/zh-CN/docs/Glossary/Accessible_description">无障碍描述</a>也要跟着更新。</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>canvas.setAttribute("aria-label", "Firefox Logo"); </code></pre></div> </li> </ol> <p>最终结果如下所示:</p> <iframe width="100%" height="260" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/5_canvas_images/index.html" loading="lazy"></iframe> <div class="notecard note"> <p><strong>备注:</strong> 完整代码可到 GitHub 下载:<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/5_canvas_images" class="external" target="_blank">5_canvas_images</a>。</p> </div></div></section><section aria-labelledby="循环和动画"><h2 id="循环和动画"><a href="#循环和动画">循环和动画</a></h2><div class="section-content"><p>目前我们学习了关于 2D 画布一些非常基础的用法,但是不学习动画你就无法体会画布的强大。画布是提供可编程图形的。如果你的作品不需要改变,那么你就只能永远面对那些静态图片了。</p></div></section><section aria-labelledby="创建循环"><h3 id="创建循环"><a href="#创建循环">创建循环</a></h3><div class="section-content"><p>在画布中使用循环是件有趣的事,你可以在 <a href="/zh-CN/docs/Web/JavaScript/Reference/Statements/for"><code>for</code></a> 循环中运行画布命令,和其他 JavaScript 代码一样。</p> <p>我们来创建一个简单的示例。</p> <ol> <li> <p>继续拷贝一份画布模板(<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template" class="external" target="_blank">1_canvas_template</a>)在代码编辑器中打开。</p> </li> <li> <p>在 JavaScript 代码末尾添加以下一行。这将创建一个新方法——<a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/translate" title="translate()"><code>translate()</code></a>,可用于移动画布的原点。</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.translate(width / 2, height / 2); </code></pre></div> <p>这会使原点 (0, 0) 从画布左上顶点移动至画布正中心。这个功能在许多场合非常实用,就像本示例,我们的绘制操作都是围绕着画布的中心点展开的。</p> </li> <li> <p>在 JavaScript 代码末尾添加以下内容:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function degToRad(degrees) { return (degrees * Math.PI) / 180; } function rand(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } let length = 250; let moveOffset = 20; for (let i = 0; i &lt; length; i++) {} </code></pre></div> <p>这里我们实现了一个与上文三角形示例中相同的 <code>degToRad()</code> 函数、一个返回给定范围内随机数 <code>rand()</code> 函数、<code>length</code> 和 <code>moveOffset</code> 变量(见下文),以及一个空的 <code>for</code> 循环。</p> </li> <li> <p>此处的理念是利用 <code>for</code> 循环在画布上循环迭代绘制好玩儿的内容。请将以下代码添加进 <code>for</code> 循环中:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillStyle = "rgba(" + (255 - length) + ", 0, " + (255 - length) + ", 0.9)"; ctx.beginPath(); ctx.moveTo(moveOffset, moveOffset); ctx.lineTo(moveOffset + length, moveOffset); const triHeight = (length / 2) * Math.tan(degToRad(60)); ctx.lineTo(moveOffset + length / 2, moveOffset + triHeight); ctx.lineTo(moveOffset, moveOffset); ctx.fill(); length--; moveOffset += 0.7; ctx.rotate(degToRad(5)); </code></pre></div> <p>在每次迭代中:</p> <ul> <li>设置 <code>fillStyle</code> 为略透明的紫色渐变色。渐变由每次迭代时 <code>length</code> 值的改变实现。随着循环的运行,<code>length</code> 值逐渐变小,从而使连续的三角形颜色逐渐变亮。</li> <li>开始路径。</li> <li>将笔移动至坐标 <code>(moveOffset, moveOffset)</code>;该变量定义了每次要绘制新三角形时需要移动的距离。</li> <li>画一条直线,终点坐标为 <code>(moveOffset+length, moveOffset)</code>。即一条长度为 <code>length</code> 与 X 轴平行的线。</li> <li>计算三角形的高,方法同上。</li> <li>向三角形底部顶点方向绘制一条直线,然后向三角形的起始点绘制一条直线。</li> <li>调用 <code>fill()</code> 为三角形填充颜色。</li> <li>更新次序变量,准备绘制下一个三角形。<code>length</code> 的值减一,使三角形每次迭代都变小一些;小幅增加 <code>moveOffset</code> 的值,使得下一个三角形略微错位;用一个新函数 <a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/rotate" title="rotate()"><code>rotate()</code></a> 来旋转整块画布,在绘制下个三角形前画布旋转 5°。</li> </ul> </li> </ol> <p>好了,最终结果如下:</p> <iframe width="100%" height="550" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/loops_animation/6_canvas_for_loop/index.html" loading="lazy"></iframe> <p>现在,你可以尝试这个示例,可以加一些创新哦。比如:</p> <ul> <li>把三角形换成矩形、弧,甚至内嵌的图片。</li> <li>修改 <code>length</code> 和 <code>moveOffset</code> 的值。</li> <li>我们引入了 <code>rand()</code> 函数但是没有使用,你可以试着用它引入一些随机数。</li> </ul> <div class="notecard note"> <p><strong>备注:</strong> 完整代码可到 GitHub 下载:<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/loops_animation/6_canvas_for_loop" class="external" target="_blank">6_canvas_for_loop</a>。</p> </div></div></section><section aria-labelledby="动画"><h3 id="动画"><a href="#动画">动画</a></h3><div class="section-content"><p>上文的循环示例很有趣,其实在重度画布应用(比如游戏或实时可视化)中恒定循环是至关重要的支持组件。如果期望画布显示的内容像一部电影,屏幕最好能够以 60 帧每秒的刷新率实时更新,这样人眼看到的动作才更真实、更平滑。</p> <p>一些 JavaScript 函数可以让函数在一秒内重复运行多次,这里最适合的就是 <a href="/zh-CN/docs/Web/API/Window/requestAnimationFrame"><code>window.requestAnimationFrame()</code></a>。它只取一个参数,即每帧要运行的函数名。下一次浏览器准备好更新屏幕时,将会调用你的函数。如果你的函数向动画中绘制了更新内容,则在函数结束前再次调用 <code>requestAnimationFrame()</code>,动画循环得以保留。只有在停止调用 <code>requestAnimationFrame()</code> 时,或 <code>requestAnimationFrame()</code> 调用后、帧调用前调用了 <a href="/zh-CN/docs/Web/API/Window/cancelAnimationFrame"><code>window.cancelAnimationFrame()</code></a> 时,循环才会停止。</p> <div class="notecard note"> <p><strong>备注:</strong> 动画结束后在主代码中调用 <code>cancelAnimationFrame()</code> 是良好习惯,可以确保不再有等待运行的更新。</p> </div> <p>浏览器自行处理诸如“使动画匀速运行”、“避免在不可见的内容浪费资源”等复杂细节问题。</p> <p>我们简单回顾一下“弹球”示例(<a href="https://mdn.github.io/learning-area/javascript/oojs/bouncing-balls/index-finished.html" class="external" target="_blank">在线运行</a>或查看<a href="https://github.com/mdn/learning-area/tree/main/javascript/oojs/bouncing-balls" class="external" target="_blank">源代码</a>),来探究一下原理。以下是让弹球持续运行的循环代码:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function loop() { ctx.fillStyle = "rgb(0 0 0 / 25%)"; ctx.fillRect(0, 0, width, height); for (const ball of balls) { ball.draw(); ball.update(); ball.collisionDetect(); } requestAnimationFrame(loop); } loop(); </code></pre></div> <p>我们在代码底部运行了一次 <code>loop()</code> 函数,它启动了整个循环,绘制了第一帧动画。接着 <code>loop()</code> 函数接管了 <code>requestAnimationFrame(loop)</code> 的调用工作,运行下一帧、再下一帧和之后的动画。</p> <p>请注意每一帧我们都完全清空画布并重新渲染所有内容。(每帧创建一个新球(25 个封顶),然后绘制每个球,更新它们的位置,检查是否撞到了其他球。)向画布中绘制的新图形不能像 DOM 元素那样单独操作。你无法再画布中单独操作某一个球,因为只要绘制完毕了,它就是画布的一部分,而不是一个单独的球。你需要擦除再重画,可以将整帧擦除再重画整个画面,也可通过编程选择最小的部分进行擦除和重画。</p> <p>优化图形动画是另一个编程主题,需要好多奇技淫巧。这超出我们的讨论范围啦。</p> <p>一般地,在画布上制作动画需要以下步骤:</p> <ol> <li>清除画布内容(可用 <a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/fillRect" title="fillRect()"><code>fillRect()</code></a> 或 <a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/clearRect" title="clearRect()"><code>clearRect()</code></a>)。</li> <li>(在需要时)用 <a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/save" title="save()"><code>save()</code></a> 保存状态。(在进行下一步前保存所更新的设置,一般在复杂环境中用到)</li> <li>绘制动画图形。</li> <li>使用 <a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/restore" title="restore()"><code>restore()</code></a> 恢复第 2 步中保存的状态。</li> <li>调用 <code>requestAnimationFrame()</code> 准备下一帧动画。</li> </ol> <div class="notecard note"> <p><strong>备注:</strong><code>save()</code> 和 <code>restore()</code> 这里暂不展开,可以访问<a href="/zh-CN/docs/Web/API/Canvas_API/Tutorial/Transformations">变形</a>教程(及后续内容)来获取详细信息。</p> </div></div></section><section aria-labelledby="一个简单的人物动画"><h3 id="一个简单的人物动画"><a href="#一个简单的人物动画">一个简单的人物动画</a></h3><div class="section-content"><p>现在我们来创建一个简单的动画,我们找来一个复古的电脑游戏的主角制作一个在屏幕上行走的动画。</p> <ol> <li> <p>继续拷贝一份画布模板(<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template" class="external" target="_blank">1_canvas_template</a>)在代码编辑器中打开。</p> </li> <li> <p>更新内部 HTML 以展示图片:</p> <div class="code-example"><div class="example-header"><span class="language-name">html</span></div><pre class="brush: html notranslate"><code>&lt;canvas class="myCanvas"&gt; &lt;p&gt;A man walking.&lt;/p&gt; &lt;/canvas&gt; </code></pre></div> </li> <li> <p>在 JS 代码末尾添加下面一行,再次将画布的原点设置为中心点。</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.translate(width / 2, height / 2); </code></pre></div> </li> <li> <p>创建一个新的 <a href="/zh-CN/docs/Web/API/HTMLImageElement"><code>HTMLImageElement</code></a> 对象,把它的 <a href="/zh-CN/docs/Web/HTML/Element/img#src"><code>src</code></a> 设置为所需图片,添加一个 <code>onload</code> 事件处理器,使 <code>draw()</code> 函数在图片载入后触发。</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const image = new Image(); image.src = "walk-right.png"; image.onload = draw; </code></pre></div> </li> <li> <p>添加一些变量,来追踪精灵图在屏幕上的位置,以及当前需要显示的精灵图的序号。</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>let sprite = 0; let posX = 0; </code></pre></div> <p>我们来解释一下“精灵图序列”(我们借鉴了麦克托马斯的<a href="https://codepen.io/mikethomas/pen/kQjKLW" class="external" target="_blank">使用 CSS 动画创建人物行走的精灵图</a>)。图片如下:</p> <p><img src="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics/walk-right.png" alt="包含六个精灵的精灵图,模拟从右侧观察一个像素化人物向前走时的不同瞬间" width="612" height="148" loading="lazy"></p> <p>图中包含六个精灵,它们组成了一趟完整的行走序列。每个精灵的尺寸为 102 × 148 像素。为了整齐的显示一个精灵,可以通过 <code>drawImage()</code> 来从序列中裁切出单独的精灵并隐藏其他部分,就像上文中操作 Firefox 图标的方法。切片的 X 坐标应为 102 的倍数,Y 坐标恒为 0。切片尺寸恒为 102 × 148 像素。</p> </li> <li> <p>在代码末尾添加一个空的 <code>draw()</code> 函数,用来添加一些代码:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function draw() {} </code></pre></div> </li> <li> <p>本部分的剩余代码都在这个 <code>draw()</code> 中展开。首先,添加以下代码,清除画布,准备绘制新的帧。注意由于我们刚才将原点设置为 <code>width/2, height/2</code>,这里需要将矩形左上顶点的坐标设置为 <code>-(width/2), -(height/2)</code>。</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.fillRect(-(width / 2), -(height / 2), width, height); </code></pre></div> </li> <li> <p>下一步,我们使用 <code>drawImage()</code>(9 参数版本)来绘制图形,添加以下代码:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>ctx.drawImage(image, sprite * 102, 0, 102, 148, 0 + posX, -74, 102, 148); </code></pre></div> <p>如你所见:</p> <ul> <li><code>image</code> 指定需要嵌入的图片。</li> <li>参数 2、3 指定切片左上顶点在原图的位置坐标,X 值为 <code>sprite</code>(精灵序列 0—5)乘 102,Y 值恒为 0。</li> <li>参数 4、5 指定切片尺寸:102 × 148 像素。</li> <li>参数 6、7 指定切片在画布绘制区域的坐上顶点坐标。X 坐标位置为 0 + <code>posX</code>,意味着我们可以通过修改 <code>posX</code> 的值来修改绘制的位置。</li> <li>参数 8、9 指定图片在画布中的尺寸。这里需要图片保持原始尺寸,因此我们指定宽、高值为 102、148。</li> </ul> </li> <li> <p>现在,我们在每帧绘制完毕(部分完毕)后修改 <code>sprite</code> 的值。在 <code>draw()</code> 函数底部添加以下内容:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>if (posX % 13 === 0) { if (sprite === 5) { sprite = 0; } else { sprite++; } } </code></pre></div> <p>将整个功能块放置在 <code>if (posX % 13 === 0) { ... }</code> 内。用“模(<code>%</code>)运算符”(即<a href="/zh-CN/docs/Web/JavaScript/Reference/Operators/Remainder">求余运算符</a>)来检测 <code>posX</code> 是否可以被 13 整除。如果整除,则通过增加 <code>sprite</code> 的值转至下一个精灵(到 5 号精灵时归零)。这实际上意味着每隔 13 帧才更新一次精灵,每秒大约更新 5 帧(<code>requestAnimationFrame()</code> 每秒最多调用 60 帧)。我们故意放慢了帧率,因为精灵图只有六个,且如果每秒显示 60 帧的话,这个角色就会快到起飞。</p> <p>外部程序块中用一个 <a href="/zh-CN/docs/Web/JavaScript/Reference/Statements/if...else"><code>if...else</code></a> 语句来检测 <code>sprite</code> 的值是否为 5(精灵序号在 0—5 间循环,因此 5 代表最后一个精灵)。如果最后一个精灵已经显示,就把 <code>sprite</code> 重置为 0,否则加 1。</p> </li> <li> <p>下一步要算出每帧 <code>posX</code> 的值,在上文代码末尾添加以下内容:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>if (posX &gt; width / 2) { let newStartPos = -(width / 2 + 102); posX = Math.ceil(newStartPos); console.log(posX); } else { posX += 2; } </code></pre></div> <p>用另一个 <code>if...else</code> 来检测 <code>posX</code> 的值是否超出了 <code>width/2</code>,那意味着角色走到了屏幕右侧边缘。如果角色没有走到屏幕边缘,只需为 <code>posX</code> 加 2。这将让它在下次绘制时更靠右些。</p> </li> <li> <p>最后,通过在 <code>draw()</code> 函数末尾添加 <a href="/zh-CN/docs/Web/API/Window/requestAnimationFrame" title="requestAnimationFrame()"><code>requestAnimationFrame()</code></a> 调用以实现动画的循环。</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>window.requestAnimationFrame(draw); </code></pre></div> </li> </ol> <p>成功了!最终效果如下所示:</p> <iframe width="100%" height="260" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation/index.html" loading="lazy"></iframe> <div class="notecard note"> <p><strong>备注:</strong> 完整代码可到 GitHub 下载:<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation" class="external" target="_blank">7_canvas_walking_animation</a>。</p> </div></div></section><section aria-labelledby="简单的绘图应用"><h3 id="简单的绘图应用"><a href="#简单的绘图应用">简单的绘图应用</a></h3><div class="section-content"><p>下面来演示一个简单的绘图应用,作为最后一个绘画示例,它将向你展示动画循环如果与用户输入(本例中为鼠标移动)结合起来。我们不会带你一步一步来实现本示例,只对代码中最有趣的部分进行探究。</p> <p>示例代码可到 GitHub 下载:<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/loops_animation/8_canvas_drawing_app" class="external" target="_blank">8_canvas_drawing_app</a>,也可在线试玩:</p> <iframe width="100%" height="600" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/loops_animation/8_canvas_drawing_app/index.html" loading="lazy"></iframe> <p>下面我们就来看看代码的精华部分。首先,用 <code>curX</code>、<code>curY</code> 和 <code>pressed</code> 这三个变量来跟踪鼠标的 X、Y 坐标和点击状态。当鼠标移动时,触发一个函数作为 <code>onmousemove</code> 事件处理器,其应捕获当前的 X 和 Y 值。再用 <code>onmousedown</code> 和 <code>onmouseup</code> 事件处理器来修改鼠标键按下时 <code>pressed</code> 的值(按下为 <code>true</code>,释放为 <code>false</code>)。</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>let curX; let curY; let pressed = false; // 更新鼠标指针坐标 document.addEventListener("mousemove", (e) =&gt; { curX = e.pageX; curY = e.pageY; }); canvas.addEventListener("mousedown", () =&gt; (pressed = true)); canvas.addEventListener("mouseup", () =&gt; (pressed = false)); </code></pre></div> <p>在按下“Clear canvas”(清除画布)按钮时,我们运行一个简单的函数来清除整个画布的内容至纯黑色,和刚才的方法一致:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>clearBtn.addEventListener("click", () =&gt; { ctx.fillStyle = "rgb(0 0 0)"; ctx.fillRect(0, 0, width, height); }); </code></pre></div> <p>这次的绘图循环非常简单,如果 <code>pressed</code> 为 <code>true</code>,则绘制一个圆,该圆以颜色选择器中设定的颜色为背景,以滑动选择器设定的数值为半径。我们必须在测量位置上方 85 像素处画圆圈,因为垂直测量是从视口顶部开始的,而我们要相对于画布顶部画圆圈(画布顶部是从 85 像素高的工具栏下方算起)。如果我们只使用 <code>curY</code> 作为 Y 坐标来绘制,那么绘制出来的圆圈会比鼠标位置低 85 个像素。</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function draw() { if (pressed) { ctx.fillStyle = colorPicker.value; ctx.beginPath(); ctx.arc( curX, curY - 85, sizePicker.value, degToRad(0), degToRad(360), false, ); ctx.fill(); } requestAnimationFrame(draw); } draw(); </code></pre></div> <p>浏览器支持所有 <a href="/zh-CN/docs/Web/HTML/Element/input"><code>&lt;input&gt;</code></a> 类型。如果某输入类型不被浏览器支持,那么它将被降格为纯文本输入。</p></div></section><section aria-labelledby="webgl"><h2 id="webgl"><a href="#webgl">WebGL</a></h2><div class="section-content"><p>2D 内容告一段落,现在简单了解一下 3D 画布。3D 画布内容可通过的 <a href="/zh-CN/docs/Web/API/WebGL_API">WebGL</a> API 实现,尽管它和 2D canvas API 都可在 <a href="/zh-CN/docs/Web/HTML/Element/canvas"><code>&lt;canvas&gt;</code></a> 元素上进行渲染,但两者是彼此独立的。</p> <p>WebGL 基于 <a href="/zh-CN/docs/Glossary/OpenGL">OpenGL</a>(开放图形库)实现,可直接与 <a href="/zh-CN/docs/Glossary/GPU">GPU</a> 通信,基于此,编写纯 WebGL 代码与常规的 JavaScript 不尽相同,更像 C++ 那样的底层语言,更加复杂,但无比强大。</p></div></section><section aria-labelledby="使用库"><h3 id="使用库"><a href="#使用库">使用库</a></h3><div class="section-content"><p>由于 3D 绘图的复杂性,大多数人写代码时会使用第三方 JavaScript 库(比如 <a href="/zh-CN/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js">Three.js</a>、<a href="/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_PlayCanvas" class="only-in-en-us">PlayCanvas</a> 或 <a href="/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Babylon.js" class="only-in-en-us">Babylon.js</a>)。大多数库的原理都基本类似,提供创建基本的、自定义性状的功能、视图定位摄影和光效、表面纹理覆盖,等等。库负责 与 WebGL 通信,你只需完成更高阶工作。</p> <p>接触任何一个库都意味着要学一套全新的 API(这里是第三方的版本),但都比使用原生 WebGL 编程简单。</p></div></section><section aria-labelledby="重新创建魔方"><h3 id="重新创建魔方"><a href="#重新创建魔方">重新创建魔方</a></h3><div class="section-content"><p>我们来看一个简单的示例,用一套 WebGL 库(这里我们选择 <a href="/zh-CN/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js">Three.js</a>,最流行的 3D 绘图库之一)来创建我们在本文开头看到的旋转魔方。</p> <ol> <li> <p>首先,新建文件夹并拷贝 <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/drawing-graphics/threejs-cube/index.html" class="external" target="_blank">threejs-cube/index.html</a> 到本地,然后拷贝 <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/drawing-graphics/threejs-cube/metal003.png" class="external" target="_blank">metal003.png</a> 到同一文件夹。图片将用作魔方的表面纹理。</p> </li> <li> <p>然后,继续在同一个文件夹内创建 <code>script.js</code> 文件。</p> </li> <li> <p>接下来,你需要安装 Three.js 库。你可参考<a href="/zh-CN/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js">使用 Three.js 构建基础演示</a>中的环境设置步骤,以便让 Three.js 如预期运行。</p> </li> <li> <p>现在 <code>three.js</code> 在页面上可用,我们可以在 <code>script.js</code> 里写 JavaScript 代码了。先从创建一个新场景开始吧——在你的 <code>script.js</code> 文件中添加以下代码:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const scene = new THREE.Scene(); </code></pre></div> <p><a href="https://threejs.org/docs/index.html#Reference/Scenes/Scene" class="external" target="_blank"><code>Scene()</code></a> 构造函数创建一个新的场景,表示即将显示的整个 3D 世界。</p> </li> <li> <p>下一步,我们需要一部<strong>摄影机</strong>来看到整个场景。在 3D 绘图语境中,摄影机表示观察者在世界里的位置,可通过下面代码创建一部摄影机:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000, ); camera.position.z = 5; </code></pre></div> <p><a href="https://threejs.org/docs/index.html#Reference/Cameras/PerspectiveCamera" class="external" target="_blank"><code>PerspectiveCamera()</code></a> 构造函数有四个参数:</p> <ul> <li>观察区域:镜头视角大小,用角度表示。</li> <li><a href="/zh-CN/docs/Glossary/Aspect_ratio">纵横比</a>:一般情况下,宽高比等于屏幕的宽比上屏幕的高。使用其他的值会使场景扭曲(也许正是你需要的,但一般都不是)。</li> <li>近裁切面:停止渲染前对象离摄影机的最近距离。设想一下,举起一个手指,逐渐移近双眼,某个点后就在也看不到这根手指了。</li> <li>远裁切面:停止渲染前离摄像机最远的对象的距离。</li> </ul> <p>将摄像机的位置设定为距 Z 轴 5 个距离单位的位置。与 CSS 类似,在屏幕之外你(观察者)的位置。</p> </li> <li> <p>第三个重要参数是渲染器。我们用它来渲染给定的场景,通过给定的摄影机观察。现在我们使用 <a href="https://threejs.org/docs/index.html#Reference/Renderers/WebGLRenderer" class="external" target="_blank"><code>WebGLRenderer()</code></a> 构造函数创建一个渲染器供稍后使用。添加以下代码:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); </code></pre></div> <p>第一行创建一个新的渲染器,第二行设定渲染器在当前摄影机视角下的尺寸,第三行将渲染好的 <a href="/zh-CN/docs/Web/HTML/Element/canvas"><code>&lt;canvas&gt;</code></a> 对象加入文档的 <a href="/zh-CN/docs/Web/HTML/Element/body"><code>&lt;body&gt;</code></a> 中。现在渲染器绘制的内容将在窗口中显示出来。</p> </li> <li> <p>下一步,在画布中创建魔方。把以下代码添加到 JavaScript 的底部:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>let cube; const loader = new THREE.TextureLoader(); loader.load("metal003.png", (texture) =&gt; { texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; texture.repeat.set(2, 2); const geometry = new THREE.BoxGeometry(2.4, 2.4, 2.4); const material = new THREE.MeshLambertMaterial({ map: texture }); cube = new THREE.Mesh(geometry, material); scene.add(cube); draw(); }); </code></pre></div> <p>内容很多,我们来剥丝抽茧:</p> <ul> <li>首先,创建一个全局变量 <code>cube</code>,这样就可以在代码任意位置访问我们的魔方。</li> <li>然后,创建一个 <a href="https://threejs.org/docs/index.html#api/zh/loaders/TextureLoader" class="external" target="_blank"><code>TextureLoader</code></a> 对象,并调用 <code>load()</code>。这里 <code>load()</code> 包含两个参数(其他情况可以有更多参数):需要调用的纹理图(PNG 文件)和纹理加载成功后调用的函数。</li> <li>函数内部,我们用 <a href="https://threejs.org/docs/index.html#api/zh/textures/Texture" class="external" target="_blank"><code>texture</code></a> 对象的属性指明我们要在魔方的每个面渲染 2 × 2 的图片,然后创建一个 <a href="https://threejs.org/docs/index.html#api/zh/geometries/BoxGeometry" class="external" target="_blank"><code>BoxGeometry</code></a> 对象和一个 <a href="https://threejs.org/docs/index.html#api/en/materials/MeshLambertMaterial" class="external" target="_blank"><code>MeshLambertMaterial</code></a> 对象,将两者作为 <a href="https://threejs.org/docs/index.html#api/zh/objects/Mesh" class="external" target="_blank"><code>Mesh</code></a> 的参数来创建我们的魔方。<a href="https://threejs.org/docs/index.html#api/zh/objects/Mesh" class="external" target="_blank"><code>Mesh</code></a> 一般就需要两个参数:一个几何(形状)和一个素材(形状表面外观)。</li> <li>最后,将魔方添加进场景中,调用我们的 <code>draw()</code> 函数启用动画。</li> </ul> </li> <li> <p>定义 <code>draw()</code> 函数前,我们需要先为场景打光,以照亮场景中的物体。请添加以下代码:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>const light = new THREE.AmbientLight("rgb(255 255 255)"); // 柔白光 scene.add(light); const spotLight = new THREE.SpotLight("rgb(255 255 255)"); spotLight.position.set(100, 1000, 1000); spotLight.castShadow = true; scene.add(spotLight); </code></pre></div> <p><a href="https://threejs.org/docs/index.html#api/zh/lights/AmbientLight" class="external" target="_blank"><code>AmbientLight</code></a> 对象是可以轻度照亮整个场景的柔光,就像户外的阳光。而 <a href="https://threejs.org/docs/index.html#api/zh/lights/SpotLight" class="external" target="_blank"><code>SpotLight</code></a> 对象是直射的硬光,就像闪光灯和手电筒(或者它的英文字面意思——聚光灯)。</p> </li> <li> <p>最后,在代码末尾添加我们的 <code>draw()</code> 函数:</p> <div class="code-example"><div class="example-header"><span class="language-name">js</span></div><pre class="brush: js notranslate"><code>function draw() { cube.rotation.x += 0.01; cube.rotation.y += 0.01; renderer.render(scene, camera); requestAnimationFrame(draw); } </code></pre></div> <p>这段代码很直观,每一帧我们都沿 X 轴 和 Y 轴将魔方轻微转动,然后按摄像机视角渲染场景,最后调用 <code>requestAnimationFrame()</code> 来准备下一帧。</p> </li> </ol> <p>回顾一下最终效果:</p> <iframe width="100%" height="500" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/threejs-cube/index.html" loading="lazy"></iframe> <p>你可以<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/threejs-cube" class="external" target="_blank">到 Github 下载完成的代码</a>。</p> <div class="notecard note"> <p><strong>备注:</strong> 在我们的 GitHub 仓库还有另一个趣味 3D 魔方示例——<a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/threejs-video-cube" class="external" target="_blank">Three.js Video Cube</a>(在线查看)。其中通过 <a href="/zh-CN/docs/Web/API/MediaDevices/getUserMedia" title="getUserMedia()"><code>getUserMedia()</code></a> 来从电脑摄像头获取一段视频,将其投影到魔方上作为纹理。</p> </div></div></section><section aria-labelledby="总结"><h2 id="总结"><a href="#总结">总结</a></h2><div class="section-content"><p>此刻你以经了解了一些 Canvas 和 WebGL 图形编程的基本理念和简单应用,你一定产生了不少创作灵感,玩得开心!</p></div></section><section aria-labelledby="参见"><h2 id="参见"><a href="#参见">参见</a></h2><div class="section-content"><p>本文我们只涉及到画布最为基本的内容,以下内容帮你探索更多:</p> <ul> <li><a href="/zh-CN/docs/Web/API/Canvas_API/Tutorial">Canvas 教程</a>:一个详尽的教程系列,更细致深入地讲解了 2D 画布所需的知识。必读。</li> <li><a href="/zh-CN/docs/Web/API/WebGL_API/Tutorial">WebGL 教程</a>:纯 WebGL 编程教程系列。</li> <li><a href="/zh-CN/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js">用 Three.js 创建一个简单的示例</a>:Three.js 基础教程。我们还提供 <a href="/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_PlayCanvas" class="only-in-en-us">PlayCanvas</a> 和 <a href="/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Babylon.js" class="only-in-en-us">Babylon.js</a> 的基础教程。</li> <li><a href="/zh-CN/docs/Games">游戏开发</a>:MDN Web 游戏开发目录页。提供与 2D、3D 画布相关的实用教程和技术,可参考“技术”和“教程”菜单项。</li> </ul></div></section><section aria-labelledby="示例"><h2 id="示例"><a href="#示例">示例</a></h2><div class="section-content"><ul> <li><a href="https://github.com/mdn/violent-theremin" class="external" target="_blank">Violent theramin</a>:用 Web 音频 API 创建声音,用画布显示漂亮的视觉效果以配合音乐。</li> <li><a href="https://github.com/mdn/voice-change-o-matic" class="external" target="_blank">Voice change-o-matic</a>:用画布为 Web 音频 API 产生的音效提供实时的视觉效果。</li> </ul> <ul class="prev-next"><li><a class="button secondary" href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Video_and_audio_APIs"><span class="button-wrap"> 上一页 </span></a></li><li><a class="button secondary" href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs"><span class="button-wrap"> 概述:客户端 Web API</span></a></li><li><a class="button secondary" href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Client-side_storage"><span class="button-wrap"> 下一页 </span></a></li></ul></div></section></article><aside class="article-footer"><div class="article-footer-inner"><div class="svg-container"><svg xmlns="http://www.w3.org/2000/svg" width="162" height="162" viewBox="0 0 162 162" fill="none" role="none"><mask id="b" fill="#fff"><path d="M97.203 47.04c8.113-7.886 18.004-13.871 28.906-17.492a78 78 0 0 1 33.969-3.39c11.443 1.39 22.401 5.295 32.024 11.411s17.656 14.28 23.476 23.86c5.819 9.579 9.269 20.318 10.083 31.385a69.85 69.85 0 0 1-5.387 32.44c-4.358 10.272-11.115 19.443-19.747 26.801-8.632 7.359-18.908 12.709-30.034 15.637l-6.17-21.698c7.666-2.017 14.746-5.703 20.694-10.773 5.948-5.071 10.603-11.389 13.606-18.467a48.14 48.14 0 0 0 3.712-22.352c-.561-7.625-2.938-15.025-6.948-21.625s-9.544-12.226-16.175-16.44-14.181-6.904-22.065-7.863a53.75 53.75 0 0 0-23.405 2.336c-7.513 2.495-14.327 6.62-19.918 12.053z"></path></mask><path stroke="url(#a)" stroke-dasharray="6, 6" stroke-width="2" d="M97.203 47.04c8.113-7.886 18.004-13.871 28.906-17.492a78 78 0 0 1 33.969-3.39c11.443 1.39 22.401 5.295 32.024 11.411s17.656 14.28 23.476 23.86c5.819 9.579 9.269 20.318 10.083 31.385a69.85 69.85 0 0 1-5.387 32.44c-4.358 10.272-11.115 19.443-19.747 26.801-8.632 7.359-18.908 12.709-30.034 15.637l-6.17-21.698c7.666-2.017 14.746-5.703 20.694-10.773 5.948-5.071 10.603-11.389 13.606-18.467a48.14 48.14 0 0 0 3.712-22.352c-.561-7.625-2.938-15.025-6.948-21.625s-9.544-12.226-16.175-16.44-14.181-6.904-22.065-7.863a53.75 53.75 0 0 0-23.405 2.336c-7.513 2.495-14.327 6.62-19.918 12.053z" mask="url(#b)" style="stroke:url(#a)" transform="translate(-63.992 -25.587)"></path><ellipse cx="8.066" cy="111.597" fill="var(--background-tertiary)" rx="53.677" ry="53.699" transform="matrix(.71707 -.697 .7243 .6895 0 0)"></ellipse><g clip-path="url(#c)" transform="translate(-63.992 -25.587)"><path fill="#9abff5" d="m144.256 137.379 32.906 12.434a4.41 4.41 0 0 1 2.559 5.667l-9.326 24.679a4.41 4.41 0 0 1-5.667 2.559l-8.226-3.108-2.332 6.17c-.466 1.233-.375 1.883-1.609 1.417l-2.253-.527c-.411-.155-.95-.594-1.206-1.161l-4.734-10.484-12.545-4.741a4.41 4.41 0 0 1-2.559-5.667l9.325-24.679a4.41 4.41 0 0 1 5.667-2.559m9.961 29.617 8.227 3.108 3.264-8.638-.498-6.768-4.113-1.555.548 7.258-4.319-1.632zm-12.339-4.663 8.226 3.108 3.264-8.637-.498-6.769-4.113-1.554.548 7.257-4.319-1.632z"></path></g><g clip-path="url(#d)" transform="translate(-63.992 -25.587)"><path fill="#81b0f3" d="M135.35 60.136 86.67 41.654c-3.346-1.27-7.124.428-8.394 3.775L64.414 81.938c-1.27 3.347.428 7.125 3.774 8.395l12.17 4.62-3.465 9.128c-.693 1.826-1.432 2.457.394 3.15l3.014 1.625c.609.231 1.637.274 2.477-.104l15.53-6.983 18.56 7.047c3.346 1.27 7.124-.428 8.395-3.775l13.862-36.51c1.27-3.346-.428-7.124-3.775-8.395M95.261 83.207l-12.17-4.62 4.852-12.779 7.19-7.017 6.085 2.31-7.725 7.51 6.389 2.426zm18.255 6.93-12.17-4.62 4.852-12.778 7.189-7.017 6.085 2.31-7.725 7.51 6.39 2.426z"></path></g><defs><clipPath id="c"><path fill="#fff" d="m198.638 146.586-65.056-24.583-24.583 65.057 65.056 24.582z"></path></clipPath><clipPath id="d"><path fill="#fff" d="m66.438 14.055 96.242 36.54-36.54 96.243-96.243-36.54z"></path></clipPath><linearGradient id="a" x1="97.203" x2="199.995" y1="47.04" y2="152.793" gradientUnits="userSpaceOnUse"><stop stop-color="#086DFC"></stop><stop offset="0.246" stop-color="#2C81FA"></stop><stop offset="0.516" stop-color="#5497F8"></stop><stop offset="0.821" stop-color="#80B0F6"></stop><stop offset="1" stop-color="#9ABFF5"></stop></linearGradient></defs></svg></div><h2>Help improve MDN</h2><fieldset class="feedback"><label>Was this page helpful to you?</label><div class="button-container"><button type="button" class="button primary has-icon yes"><span class="button-wrap"><span class="icon icon-thumbs-up "></span>Yes</span></button><button type="button" class="button primary has-icon no"><span class="button-wrap"><span class="icon icon-thumbs-down "></span>No</span></button></div></fieldset><a class="contribute" href="https://github.com/mdn/translated-content/blob/main/CONTRIBUTING.md" title="This will take you to our contribution guidelines on GitHub." target="_blank" rel="noopener noreferrer">Learn how to contribute</a>.<p class="last-modified-date">This page was last modified on<!-- --> <time dateTime="2024-12-21T13:03:41.000Z">2024年12月21日</time> by<!-- --> <a href="/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics/contributors.txt" rel="nofollow">MDN contributors</a>.</p><div id="on-github" class="on-github"><a href="https://github.com/mdn/translated-content/blob/main/files/zh-cn/learn_web_development/extensions/client-side_apis/drawing_graphics/index.md?plain=1" title="Folder: zh-cn/learn_web_development/extensions/client-side_apis/drawing_graphics (Opens in a new tab)" target="_blank" rel="noopener noreferrer">View this page on GitHub</a> <!-- -->•<!-- --> <a href="https://github.com/mdn/translated-content/issues/new?template=page-report-zh-cn.yml&amp;mdn-url=https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FLearn_web_development%2FExtensions%2FClient-side_APIs%2FDrawing_graphics&amp;metadata=%3C%21--+Do+not+make+changes+below+this+line+--%3E%0A%3Cdetails%3E%0A%3Csummary%3EPage+report+details%3C%2Fsummary%3E%0A%0A*+Folder%3A+%60zh-cn%2Flearn_web_development%2Fextensions%2Fclient-side_apis%2Fdrawing_graphics%60%0A*+MDN+URL%3A+https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FLearn_web_development%2FExtensions%2FClient-side_APIs%2FDrawing_graphics%0A*+GitHub+URL%3A+https%3A%2F%2Fgithub.com%2Fmdn%2Ftranslated-content%2Fblob%2Fmain%2Ffiles%2Fzh-cn%2Flearn_web_development%2Fextensions%2Fclient-side_apis%2Fdrawing_graphics%2Findex.md%0A*+Last+commit%3A+https%3A%2F%2Fgithub.com%2Fmdn%2Ftranslated-content%2Fcommit%2F216c73b669a9760f7a444ebfbcb04a68bb3669d9%0A*+Document+last+modified%3A+2024-12-21T13%3A03%3A41.000Z%0A%0A%3C%2Fdetails%3E" title="This will take you to GitHub to file a new issue." target="_blank" rel="noopener noreferrer">Report a problem with this content</a></div></div></aside></main></div></div><footer id="nav-footer" class="page-footer"><div class="page-footer-grid"><div class="page-footer-logo-col"><a href="/" class="mdn-footer-logo" aria-label="MDN homepage"><svg width="48" height="17" viewBox="0 0 48 17" fill="none" xmlns="http://www.w3.org/2000/svg"><title id="mdn-footer-logo-svg">MDN logo</title><path d="M20.04 16.512H15.504V10.416C15.504 9.488 15.344 8.824 15.024 8.424C14.72 8.024 14.264 7.824 13.656 7.824C12.92 7.824 12.384 8.064 12.048 8.544C11.728 9.024 11.568 9.64 11.568 10.392V14.184H13.008V16.512H8.472V10.416C8.472 9.488 8.312 8.824 7.992 8.424C7.688 8.024 7.232 7.824 6.624 7.824C5.872 7.824 5.336 8.064 5.016 8.544C4.696 9.024 4.536 9.64 4.536 10.392V14.184H6.6V16.512H0V14.184H1.44V8.04H0.024V5.688H4.536V7.32C5.224 6.088 6.32 5.472 7.824 5.472C8.608 5.472 9.328 5.664 9.984 6.048C10.64 6.432 11.096 7.016 11.352 7.8C11.992 6.248 13.168 5.472 14.88 5.472C15.856 5.472 16.72 5.776 17.472 6.384C18.224 6.992 18.6 7.936 18.6 9.216V14.184H20.04V16.512Z" fill="currentColor"></path><path d="M33.6714 16.512H29.1354V14.496C28.8314 15.12 28.3834 15.656 27.7914 16.104C27.1994 16.536 26.4154 16.752 25.4394 16.752C24.0154 16.752 22.8954 16.264 22.0794 15.288C21.2634 14.312 20.8554 12.984 20.8554 11.304C20.8554 9.688 21.2554 8.312 22.0554 7.176C22.8554 6.04 24.0634 5.472 25.6794 5.472C26.5594 5.472 27.2794 5.648 27.8394 6C28.3994 6.352 28.8314 6.8 29.1354 7.344V2.352H26.9754V0H32.2314V14.184H33.6714V16.512ZM29.1354 11.04V10.776C29.1354 9.88 28.8954 9.184 28.4154 8.688C27.9514 8.176 27.3674 7.92 26.6634 7.92C25.9754 7.92 25.3674 8.176 24.8394 8.688C24.3274 9.2 24.0714 10.008 24.0714 11.112C24.0714 12.152 24.3114 12.944 24.7914 13.488C25.2714 14.032 25.8394 14.304 26.4954 14.304C27.3114 14.304 27.9514 13.96 28.4154 13.272C28.8954 12.584 29.1354 11.84 29.1354 11.04Z" fill="currentColor"></path><path d="M47.9589 16.512H41.9829V14.184H43.4229V10.416C43.4229 9.488 43.2629 8.824 42.9429 8.424C42.6389 8.024 42.1829 7.824 41.5749 7.824C40.8389 7.824 40.2709 8.056 39.8709 8.52C39.4709 8.968 39.2629 9.56 39.2469 10.296V14.184H40.6869V16.512H34.7109V14.184H36.1509V8.04H34.5909V5.688H39.2469V7.344C39.9669 6.096 41.1269 5.472 42.7269 5.472C43.7509 5.472 44.6389 5.776 45.3909 6.384C46.1429 6.992 46.5189 7.936 46.5189 9.216V14.184H47.9589V16.512Z" fill="currentColor"></path></svg></a><p>Your blueprint for a better internet.</p><ul class="social-icons"><li><a href="https://mastodon.social/@mdn" target="_blank" rel="me noopener noreferrer"><span class="icon icon-mastodon"></span><span class="visually-hidden">MDN on Mastodon</span></a></li><li><a href="https://twitter.com/mozdevnet" target="_blank" rel="noopener noreferrer"><span class="icon icon-twitter-x"></span><span class="visually-hidden">MDN on X (formerly Twitter)</span></a></li><li><a href="https://github.com/mdn/" target="_blank" rel="noopener noreferrer"><span class="icon icon-github-mark-small"></span><span class="visually-hidden">MDN on GitHub</span></a></li><li><a href="/en-US/blog/rss.xml" target="_blank"><span class="icon icon-feed"></span><span class="visually-hidden">MDN Blog RSS Feed</span></a></li></ul></div><div class="page-footer-nav-col-1"><h2 class="footer-nav-heading">MDN</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a href="/en-US/about">About</a></li><li class="footer-nav-item"><a href="/en-US/blog/">Blog</a></li><li class="footer-nav-item"><a href="https://www.mozilla.org/en-US/careers/listings/?team=ProdOps" target="_blank" rel="noopener noreferrer">Careers</a></li><li class="footer-nav-item"><a href="/en-US/advertising">Advertise with us</a></li></ul></div><div class="page-footer-nav-col-2"><h2 class="footer-nav-heading">Support</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a class="footer-nav-link" href="https://support.mozilla.org/products/mdn-plus">Product help</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/zh-CN/docs/MDN/Community/Issues">Report an issue</a></li></ul></div><div class="page-footer-nav-col-3"><h2 class="footer-nav-heading">Our communities</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a class="footer-nav-link" href="/en-US/community">MDN Community</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="https://discourse.mozilla.org/c/mdn/236" target="_blank" rel="noopener noreferrer">MDN Forum</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/discord" target="_blank" rel="noopener noreferrer">MDN Chat</a></li></ul></div><div class="page-footer-nav-col-4"><h2 class="footer-nav-heading">Developers</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a class="footer-nav-link" href="/zh-CN/docs/Web">Web Technologies</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/zh-CN/docs/Learn">Learn Web Development</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/zh-CN/plus">MDN Plus</a></li><li class="footer-nav-item"><a href="https://hacks.mozilla.org/" target="_blank" rel="noopener noreferrer">Hacks Blog</a></li></ul></div><div class="page-footer-moz"><a href="https://www.mozilla.org/" class="footer-moz-logo-link" target="_blank" rel="noopener noreferrer"><svg xmlns="http://www.w3.org/2000/svg" width="137" height="32" fill="none" viewBox="0 0 267.431 62.607"><path fill="currentColor" d="m13.913 23.056 5.33 25.356h2.195l5.33-25.356h14.267v38.976h-7.578V29.694h-2.194l-7.264 32.337h-7.343L9.418 29.694H7.223v32.337H-.354V23.056Zm47.137 9.123c9.12 0 14.423 5.385 14.423 15.214s-5.33 15.214-14.423 15.214c-9.12 0-14.423-5.385-14.423-15.214 0-9.855 5.304-15.214 14.423-15.214m0 24.363c4.285 0 6.428-2.196 6.428-7.032v-4.287c0-4.836-2.143-7.032-6.428-7.032s-6.428 2.196-6.428 7.032v4.287c0 4.836 2.143 7.032 6.428 7.032m18.473-.157 15.47-18.01h-15.26v-5.647h24.352v5.646L88.616 56.385h15.704v5.646H79.523Zm29.318-23.657h11.183V62.03h-7.578V38.375h-3.632v-5.646zm3.605-9.672h7.578v5.646h-7.578zm13.17 0h11.21v38.976h-7.578v-33.33h-3.632zm16.801 0H153.6v38.976h-7.577v-33.33h-3.632v-5.646zm29.03 9.123c4.442 0 7.394 2.143 8.231 5.881h2.194v-5.332h9.276v5.646h-3.632v18.011h3.632v5.646h-4.442c-3.135 0-4.834-1.699-4.834-4.836V56.7h-2.194c-.81 3.738-3.789 5.881-8.23 5.881-6.978 0-11.916-5.829-11.916-15.214 0-9.384 4.938-15.187 11.915-15.187m2.3 24.363c4.284 0 6.192-2.196 6.192-7.032v-4.287c0-4.836-1.908-7.032-6.193-7.032-4.18 0-6.193 2.196-6.193 7.032v4.287c0 4.836 2.012 7.032 6.193 7.032m48.34 5.489h-7.577V0h7.577zm6.585-29.643h32.165v-2.196l-21.295-7.634v-6.143l21.295-7.633V6.588h-25.345V0h32.165v12.522l-17.35 5.881V20.6l17.35 5.882v12.521h-38.985zm0-25.801h6.794v6.796h-6.794z"></path></svg></a><ul class="footer-moz-list"><li class="footer-moz-item"><a href="https://www.mozilla.org/privacy/websites/" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Website Privacy Notice</a></li><li class="footer-moz-item"><a href="https://www.mozilla.org/privacy/websites/#cookies" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Cookies</a></li><li class="footer-moz-item"><a href="https://www.mozilla.org/about/legal/terms/mozilla" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Legal</a></li><li class="footer-moz-item"><a href="https://www.mozilla.org/about/governance/policies/participation/" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Community Participation Guidelines</a></li></ul></div><div class="page-footer-legal"><p id="license" class="page-footer-legal-text">Visit<!-- --> <a href="https://www.mozilla.org" target="_blank" rel="noopener noreferrer">Mozilla Corporation’s</a> <!-- -->not-for-profit parent, the<!-- --> <a target="_blank" rel="noopener noreferrer" href="https://foundation.mozilla.org/">Mozilla Foundation</a>.<br/>Portions of this content are ©1998–<!-- -->2025<!-- --> by individual mozilla.org contributors. Content available under<!-- --> <a href="/zh-CN/docs/MDN/Writing_guidelines/Attrib_copyright_license">a Creative Commons license</a>.</p></div></div></footer></div><script type="application/json" id="hydration">{"url":"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics","doc":{"body":[{"type":"prose","value":{"id":null,"title":null,"isH3":false,"content":"<ul class=\"prev-next\"><li><a class=\"button secondary\" href=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Video_and_audio_APIs\"><span class=\"button-wrap\"> 上一页 </span></a></li><li><a class=\"button secondary\" href=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs\"><span class=\"button-wrap\"> 概述:客户端 Web API</span></a></li><li><a class=\"button secondary\" href=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Client-side_storage\"><span class=\"button-wrap\"> 下一页 </span></a></li></ul>\n<p>浏览器包含一些非常强大的图形编程工具,从可缩放矢量图形(Scalable Vector Graphics,简称 <a href=\"/zh-CN/docs/Web/SVG\">SVG</a>)语言到用于在 HTML <a href=\"/zh-CN/docs/Web/HTML/Element/canvas\"><code>&lt;canvas&gt;</code></a> 元素上绘制图形的 API(参见 <a href=\"/zh-CN/docs/Web/API/Canvas_API\">Canvas API</a> 和 <a href=\"/zh-CN/docs/Web/API/WebGL_API\">WebGL</a>)。本文将介绍 canvas,并提供更多的学习资源。</p>\n<figure class=\"table-container\"><table>\n <tbody>\n <tr>\n <th scope=\"row\">前提:</th>\n <td>\n JavaScript 基础(见\n <a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting\">JavaScript 第一步</a>、<a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting\">创建 JavaScript 代码块</a>、<a href=\"/zh-CN/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects\">JavaScript 对象</a>)、<a href=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Introduction\">客户端 API 基础知识</a>。\n </td>\n </tr>\n <tr>\n <th scope=\"row\">目标:</th>\n <td>\n 学习 JavaScript 在 <code>&lt;canvas&gt;</code> 元素中绘图的基础知识。\n </td>\n </tr>\n </tbody>\n</table></figure>"}},{"type":"prose","value":{"id":"web_中的图形","title":"Web 中的图形","isH3":false,"content":"<p>我们来讨论 HTML 的<a href=\"/zh-CN/docs/Learn_web_development/Core/Structuring_content\">多媒体和嵌入</a>模块,早先的网页只有单调的文字,后来才引入了图像,起初是通过 <a href=\"/zh-CN/docs/Web/HTML/Element/img\"><code>&lt;img&gt;</code></a> 元素的方式,后来出现了类似于 <a href=\"/zh-CN/docs/Web/CSS/background-image\"><code>background-image</code></a> 的 CSS 属性和 <a href=\"/zh-CN/docs/Web/SVG\">SVG</a> 图像等方式。</p>\n<p>然而,这还不够好。当你能够使用 <a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics\">CSS</a> 和 <a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting\">JavaScript</a> 让 SVG 矢量图(因为其由标记表示)动起来时,位图却依然没有相应的支持。同时 SVG 动画的可用工具也少得可怜。Web 仍然无法高效地创建动画、游戏、3D 场景,而其他需求则通常由底层语言(如 C++ 或 Java)来应对。</p>\n<p>当浏览器于 2004 年开始支持 HTML 画布元素 <a href=\"/zh-CN/docs/Web/HTML/Element/canvas\"><code>&lt;canvas&gt;</code></a> 和相关的 <a href=\"/zh-CN/docs/Web/API/Canvas_API\">Canvas API</a> 时,形势开始改善。下面你会看到,canvas 提供了许多用于创建 2D 动画、游戏、数据可视化和其他应用类型的有用工具,特别是当捆绑了由 Web 平台提供的一些其他的 API 时。</p>\n<p>下面的例子显示的是一个基于 canvas 的简单的 2D 弹跳球动画,前面我们在<a href=\"/zh-CN/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_building_practice\">介绍 JavaScript 对象</a>模块中见到过。</p>\n<iframe width=\"100%\" height=\"500\" src=\"https://mdn.github.io/learning-area/javascript/oojs/bouncing-balls/index-finished.html\" loading=\"lazy\"></iframe>\n<p>大约在 2006—2007 年,Mozilla 开始测试 3D 画布实现。后来演化为 <a href=\"/zh-CN/docs/Web/API/WebGL_API\">WebGL</a>,它获得了各大浏览器厂商的认可,于是大约在 2009—2010 年间得到了标准化。WebGL 可以让你在 Web 浏览器中生成真正的 3D 图形。下面的例子显示了一个简单的旋转的 WebGL 立方体:</p>\n<iframe width=\"100%\" height=\"500\" src=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/threejs-cube/index.html\" loading=\"lazy\"></iframe>\n<p>由于原生的 WebGL 代码非常复杂,本文主要针对 2D 画布。然而,你也可以通过<a href=\"/zh-CN/docs/Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL\">初识 WebGL</a> 找到 WebGL 原生代码的教程,来学习如何更容易地使用 WebGL 库来创建一个 3D 场景。</p>"}},{"type":"prose","value":{"id":"主动学习:开始使用_canvas","title":"主动学习:开始使用 &lt;canvas&gt;","isH3":false,"content":"<p>要在网页中创建 2D <em>或</em> 3D 场景,必须从 HTML <a href=\"/zh-CN/docs/Web/HTML/Element/canvas\"><code>&lt;canvas&gt;</code></a> 元素开始。该元素用于定义页面中的绘图区域。这与在页面中包含元素一样简单:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">html</span></div><pre class=\"brush: html notranslate\"><code>&lt;canvas width=\"320\" height=\"240\"&gt;&lt;/canvas&gt;\n</code></pre></div>\n<p>网页中会生成一块 320 × 240 像素的画布。</p>\n<p>在 <code>&lt;canvas&gt;</code> 标签内,你可以放置一些回退内容。这会向不支持画布的浏览器或屏幕阅读器的用户描述画布内容。</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">html</span></div><pre class=\"brush: html notranslate\"><code>&lt;canvas width=\"320\" height=\"240\"&gt;\n &lt;p&gt;为无法查看画布的用户提供的画布描述。&lt;/p&gt;\n&lt;/canvas&gt;\n</code></pre></div>\n<p>回退内容应为画布内容提供有用的替代内容。比如,如果你要渲染不断更新的股价曲线图,回退内容可以是最新股价图的静态图像,并带有替代文本(以文本的形式说明价格)或指向各个股票页面的链接列表。</p>\n<div class=\"notecard note\">\n<p><strong>备注:</strong>\n屏幕阅读器无法获取画布内容。需要在画布元素的 <a href=\"/zh-CN/docs/Web/Accessibility/ARIA/Attributes/aria-label\"><code>aria-label</code></a> 属性填写描述性文本,或者在起始和结束的 <code>&lt;canvas&gt;</code> 标签里嵌入回退内容。画布内容不是 DOM 的一部分,但嵌入的回退内容是 DOM 的一部分。</p>\n</div>"}},{"type":"prose","value":{"id":"创建画布并确定尺寸","title":"创建画布并确定尺寸","isH3":true,"content":"<p>让我们开始吧:创建画布,准备尝试绘制图形。</p>\n<ol>\n<li>\n<p>首先在本地拷贝一份 <a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/0_canvas_start\" class=\"external\" target=\"_blank\">0_canvas_start</a> 文件夹。包含三个文件:</p>\n<ul>\n<li>“index.html”</li>\n<li>“script.js”</li>\n<li>“style.css”</li>\n</ul>\n</li>\n<li>\n<p>打开“index.html”,在 <a href=\"/zh-CN/docs/Web/HTML/Element/body\"><code>&lt;body&gt;</code></a> 的起始标签下面填加以下代码。</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">html</span></div><pre class=\"brush: html notranslate\"><code>&lt;canvas class=\"myCanvas\"&gt;\n &lt;p&gt;添加恰当的回退信息。&lt;/p&gt;\n&lt;/canvas&gt;\n</code></pre></div>\n<p>我们为 <code>&lt;canvas&gt;</code> 元素添加了一个 <code>class</code>,使得在网页中选择多个画布时会容易些。这里我们移除了 <code>width</code> 和 <code>height</code> 属性(你可以随时添上,但是我们会在下方用 JavaScript 把它们添加回来)。不明确指定宽高时,画布默认尺寸为 300 × 150 像素。</p>\n</li>\n<li>\n<p>现在,打开“script.js”并添加以下 JavaScript 代码:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const canvas = document.querySelector(\".myCanvas\");\nconst width = (canvas.width = window.innerWidth);\nconst height = (canvas.height = window.innerHeight);\n</code></pre></div>\n<p>这里我们用 <code>canvas</code> 常量来存储画布的引用。第二行中我们将 <a href=\"/zh-CN/docs/Web/API/Window/innerWidth\"><code>Window.innerWidth</code></a>(可视区域宽度)赋值给一个新常量 <code>width</code> 和画布的 <code>width</code> 属性。第三行中,我们将 <a href=\"/zh-CN/docs/Web/API/Window/innerHeight\"><code>Window.innerHeight</code></a>(可视区域高度)赋值给一个新常量 <code>height</code> 和画布的 <code>height</code> 属性。然后我们就得到了一个充满浏览器窗口的画布。</p>\n<p>你还可以看到我们使用了多个等号来进行链式赋值,这在 JavaScript 中是允许的,很适合为多个变量同时赋一个相同的值。我们想要使用 width/height 变量来更方便地访问画布的宽和高,因为它们是后面很有用的值(例如,你想在画布宽度的一半处绘制某个东西)。</p>\n</li>\n</ol>\n<div class=\"notecard note\">\n<p><strong>备注:</strong>\n如上文所讲,一般情况下图片的尺寸可以通过 HTML 属性或 DOM 属性来设定。你也可以使用 CSS,但问题是大小调整是在画布渲染后进行的,就会与其他图像一样(其实渲染好的画布就是一幅图片),所显示的内容将变得像素化或扭曲变形。</p>\n</div>"}},{"type":"prose","value":{"id":"获取画布上下文并完成设置","title":"获取画布上下文并完成设置","isH3":true,"content":"<p>画布模板设置还有最后一步。我们需要获得一个对绘画区域的特殊的引用(称为“上下文”)用来在画布上绘图。可通过 <a href=\"/zh-CN/docs/Web/API/HTMLCanvasElement/getContext\"><code>HTMLCanvasElement.getContext()</code></a> 方法获得基础的绘画功能,需要提供一个字符串参数来表示所需上下文的类型。</p>\n<p>这里我们需要一个 2d 画布,在“script.js”中添加以下 JavaScript 代码:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const ctx = canvas.getContext(\"2d\");\n</code></pre></div>\n<div class=\"notecard note\">\n<p><strong>备注:</strong>\n可选上下文还包括 WebGL(<code>webgl</code>)、WebGL 2(<code>webgl2</code>)等等,但本文暂不涉及。</p>\n</div>\n<p>好啦,我们的画布现在已经准备好并可以绘制了!<code>ctx</code> 变量包含一个 <a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D\"><code>CanvasRenderingContext2D</code></a> 对象,画布上所有绘画操作都会涉及到这个对象。</p>\n<p>在继续之前,我们先做最后一件事。我们将画布背景涂成黑色,让你初步体验 canvas API。在 JavaScript 底部添加以下几行:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillStyle = \"rgb(0, 0, 0)\";\nctx.fillRect(0, 0, width, height);\n</code></pre></div>\n<p>这里我们使用画布的 <a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/fillStyle\" title=\"fillStyle\"><code>fillStyle</code></a> 属性(和 CSS 属性<a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Values_and_units#颜色\">色值</a>一致)设置填充色,然后使用 <a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/fillRect\" title=\"fillRect\"><code>fillRect</code></a> 方法绘制一个覆盖整个区域的矩形(前两个参数是矩形左上顶点的坐标,后两个参数是矩形的长宽,现在你知道 <code>width</code> 和 <code>height</code> 的作用了吧)。</p>\n<p>好的,模板已经就位,我们要开始了。</p>"}},{"type":"prose","value":{"id":"2d_画布基础","title":"2D 画布基础","isH3":false,"content":"<p>如上文所讲,所有绘画操作都离不开 <a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D\"><code>CanvasRenderingContext2D</code></a> 对象(这里叫做 <code>ctx</code>)。许多操作都需要提供坐标来指示绘图的确切位置。画布左上角的坐标是 (0, 0),横坐标(x)轴向右延伸,纵坐标(y)轴向下延伸。</p>\n<p><img src=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics/canvas_default_grid.png\" alt=\"小方块网格图,中间为钢蓝色方块。画布的左上角是画布 x 轴和 y 轴的原点(0,0)。水平(x)轴从左到右表示宽度,垂直(y)轴从上到下表示高度。蓝色方块的左上角被标注为距离 y 轴 x 个单位,距离 x 轴 y 个单位。\" width=\"220\" height=\"220\" loading=\"lazy\"></p>\n<p>绘图操作可基于矩形原语实现,也可通过追踪一个特定路径后填充颜色实现。下面分别讲解。</p>"}},{"type":"prose","value":{"id":"简单矩形","title":"简单矩形","isH3":true,"content":"<p>让我们从简单矩形开始。</p>\n<ol>\n<li>\n<p>首先,复制一份刚才创建的画布模板(如果你没有按上述步骤进行,请在本地创建 <a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template\" class=\"external\" target=\"_blank\">1_canvas_template</a> 目录的副本)。</p>\n</li>\n<li>\n<p>然后在 JavaScript 末尾添加以下几行:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillStyle = \"rgb(255, 0, 0)\";\nctx.fillRect(50, 50, 100, 150);\n</code></pre></div>\n<p>保存并刷新,画布上将出现一个红色的矩形。其左边和顶边与画布边缘距离均为 50 像素(由前两个参数指定),宽 100 像素、高 150 像素(由后两个参数指定)。</p>\n</li>\n<li>\n<p>然后再添加一个绿色矩形。在 JavaScript 末尾添加以下行:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillStyle = \"rgb(0, 255, 0)\";\nctx.fillRect(75, 75, 100, 100);\n</code></pre></div>\n<p>保存并刷新,新的矩形就会出现。这里引出了一个新问题:绘制矩形、线等操作按出现的顺序依次进行。就像粉刷墙面时,两层重叠时新层总会覆盖旧层。这一点是无法改变的,因此在绘制图形时一定要慎重考虑顺序问题。</p>\n</li>\n<li>\n<p>你还可以通过指定半透明的颜色来绘制半透明的图形,比如使用 <code>rgba()</code>。“α 通道”定义了颜色的透明度。值越高透明度越高,底层的内容就越清晰。在代码中添加以下行:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillStyle = \"rgba(255, 0, 255, 0.75)\";\nctx.fillRect(25, 100, 175, 50);\n</code></pre></div>\n</li>\n<li>\n<p>现在你可以自己尝试绘制一些矩形了,玩得开心!</p>\n</li>\n</ol>"}},{"type":"prose","value":{"id":"描边和线宽","title":"描边和线宽","isH3":true,"content":"<p>目前我们绘制的矩形都是填充颜色的,我们也可以绘制仅包含外部框线(图形设计中称为<strong>描边</strong>)的矩形。你可以使用 <a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/strokeStyle\" title=\"strokeStyle\"><code>strokeStyle</code></a> 属性来设置描边颜色,使用 <a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/strokeRect\" title=\"strokeRect\"><code>strokeRect</code></a> 来绘制一个矩形的轮廓。</p>\n<ol>\n<li>\n<p>在上文的 JavaScript 代码的末尾添加以下行:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.strokeStyle = \"rgb(255, 255, 255)\";\nctx.strokeRect(25, 25, 175, 200);\n</code></pre></div>\n</li>\n<li>\n<p>默认的描边宽度是 1 像素,可以通过调整 <a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/lineWidth\" title=\"lineWidth\"><code>lineWidth</code></a> 属性(接受一个表示描边宽度像素值的数字)的值来修改。在上文两行后添加以下行:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.lineWidth = 5;\n</code></pre></div>\n</li>\n</ol>\n<p>现在可以看到白色的外边框变得更粗了。就这么简单,示例看上去像这样:</p>\n<iframe width=\"100%\" height=\"250\" src=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/2_canvas_rectangles/index.html\" loading=\"lazy\"></iframe>\n<div class=\"notecard note\">\n<p><strong>备注:</strong>\n完整代码请访问 GitHub:<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/2_canvas_rectangles\" class=\"external\" target=\"_blank\">2_canvas_rectangles</a>。</p>\n</div>"}},{"type":"prose","value":{"id":"绘制路径","title":"绘制路径","isH3":true,"content":"<p>可以通过绘制路径来绘制比矩形更复杂的图形。路径中至少要包含笔运行精确路径的代码以确定图形的形状。画布提供了许多函数用来绘制直线、圆、贝塞尔曲线,等等。</p>\n<p>让我们从复制画布模板(<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template\" class=\"external\" target=\"_blank\">1_canvas_template</a>)开始这一部分,以便在其中绘制新的示例。</p>\n<p>一些通用的方法和属性将贯穿以下全部内容:</p>\n<ul>\n<li><a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/beginPath\" title=\"beginPath()\"><code>beginPath()</code></a>:在笔当前所在位置开始绘制一条路径。在新的画布中,笔起始位置为 (0, 0)。</li>\n<li><a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/moveTo\" title=\"moveTo()\"><code>moveTo()</code></a>:将笔移动至另一个坐标点,不记录、不留痕迹,只将笔“跳”至新位置。</li>\n<li><a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/fill\" title=\"fill()\"><code>fill()</code></a>:通过为当前所绘制路径的区域填充颜色来绘制一个新的填充形状。</li>\n<li><a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/stroke\" title=\"stroke()\"><code>stroke()</code></a>:通过为当前绘制路径的区域描边,来绘制一个只有边框的形状。</li>\n<li>路径也可和矩形一样使用 <code>lineWidth</code> 和 <code>fillStyle</code> / <code>strokeStyle</code> 等特性。</li>\n</ul>\n<p>以下是一个典型的简单路径绘制操作:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillStyle = \"rgb(255, 0, 0)\";\nctx.beginPath();\nctx.moveTo(50, 50);\n// 绘制路径\nctx.fill();\n</code></pre></div>\n<h4 id=\"画线\">画线</h4>\n<p>我们来在画布上绘制一个等边三角形。</p>\n<ol>\n<li>\n<p>首先,在代码底部添加下面的辅助函数。它可以将角度换算为弧度,在为 JavaScript 提供角度值时非常实用,JavaScript 基本上只接受弧度值,而人类更习惯用角度值。</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function degToRad(degrees) {\n return (degrees * Math.PI) / 180;\n}\n</code></pre></div>\n</li>\n<li>\n<p>然后,在刚才复制好的文件中添加下面的内容,以开始路径的绘制。此处为我们为三角形设置了颜色,准备绘制,然后将笔移动至 (50, 50)(没有绘制任何内容)。然后准备在新的坐标开始绘制三角形。</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillStyle = \"rgb(255, 0, 0)\";\nctx.beginPath();\nctx.moveTo(50, 50);\n</code></pre></div>\n</li>\n<li>\n<p>接下来在脚本中添加以下代码:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.lineTo(150, 50);\nconst triHeight = 50 * Math.tan(degToRad(60));\nctx.lineTo(100, 50 + triHeight);\nctx.lineTo(50, 50);\nctx.fill();\n</code></pre></div>\n<p>我们来逐行解释:</p>\n<p>首先绘制一条直线,终点坐标为 (150, 50)。此时路径沿 x 轴向右行走 100 像素。</p>\n<p>然后利用三角函数来计算等边三角形的高。这里我们要绘制的三角形是朝下的。等边三角形每个角均为 60°,为计算高的值,我们可以将三角形从正中心分割为两个直角三角形,每个直角三角形的三个角分别为 90°、60°、30°。对于边:</p>\n<ul>\n<li>最长的边称为<strong>斜边</strong>。</li>\n<li>紧挨 60° 角的边称为<strong>临边</strong>,显然地,它的长度是刚才绘制的线的一半,即 50 像素。</li>\n<li>60° 角对面的边称为<strong>对边</strong>,即三角形的高,需要计算得到。</li>\n</ul>\n<p><img src=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics/trigonometry.png\" alt=\"一个指向下方的等边三角形,标有角和边。顶部的水平线标注“临边”。从相邻线中间开始的一条垂直虚线标注“对边”,它将三角形分割成两个相等的直角三角形。三角形的右边被标为“斜边”,因为它是标有“对边”的直线所构成的直角三角形的斜边。等边三角形三边的长度相等,同时斜边是直角三角形最长的边。\" width=\"200\" height=\"166\" loading=\"lazy\"></p>\n<p>通过基本三角函数可得:临边长度乘以角的正切等于对边长度。于是可得三角形的高为 <code>50 * Math.tan(degToRad(60))</code>。由于 <a href=\"/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/tan\"><code>Math.tan()</code></a> 接受数值的单位为弧度,于是我们用刚才的 <code>degToRad()</code> 函数将 60° 换算为弧度。</p>\n</li>\n<li>\n<p>有了三角形的高,我们来绘制另一条线,终点坐标为 <code>(100, 50+triHeight)</code>。X 坐标值很简单,应在刚才绘制的水平线两顶点正中间位置。Y 值应为 50 加上三角形的高,因为高即三角形底边到顶点的距离。</p>\n</li>\n<li>\n<p>下一条线的终点坐标为绘制整个三角形的起点坐标。</p>\n</li>\n<li>\n<p>最后,运行 <code>ctx.fill()</code> 来终止路径,并为图形填充颜色。</p>\n</li>\n</ol>\n<h4 id=\"画圆\">画圆</h4>\n<p>下面来看可在画布中绘制圆的方法——<a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/arc\" title=\"arc()\"><code>arc()</code></a>,通过连续的点来绘制整个圆或者弧(arc,即局部的圆)。</p>\n<ol>\n<li>\n<p>在代码底部添加以下几行,以向画布中添加一条弧。</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillStyle = \"rgb(0, 0, 255)\";\nctx.beginPath();\nctx.arc(150, 106, 50, degToRad(0), degToRad(360), false);\nctx.fill();\n</code></pre></div>\n<p><code>arc()</code> 有六个参数。前两个指定圆心的位置坐标,第三个是圆的半径,第四、五个是绘制弧的起、止角度(给定 0° 和 360° 便能绘制一个完整的圆),第六个参数指定圆是应该逆时针绘制还是顺时针绘制(<code>false</code> 是顺时针)。</p>\n<div class=\"notecard note\">\n<p><strong>备注:</strong>\n0° 设定为水平向右。</p>\n</div>\n</li>\n<li>\n<p>我们再来画一条弧:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillStyle = \"yellow\";\nctx.beginPath();\nctx.arc(200, 106, 50, degToRad(-45), degToRad(45), true);\nctx.lineTo(200, 106);\nctx.fill();\n</code></pre></div>\n<p>模式基本一样,但有两点不同:</p>\n<ul>\n<li>将 <code>arc()</code> 的最后一个参数设置为 <code>true</code>,意味着弧将逆时针绘制,也就意味着即使起、止角度分别设置为 -45°、45°,我们还是得到了区域外的一条 270° 的弧。如果把 <code>true</code> 改为 <code>false</code> 重新运行,将得到 90° 的弧。</li>\n<li>在调用 <code>fill()</code> 前,我们绘制了一条终点为圆心的直线。然后我们就渲染出一个惟妙惟肖的吃豆人模型。如果删除这条线(试试呗)再重新运行代码,你只能得到一个起止点间被砍掉一块的圆。这向我们展示了画布的另一个重要事项:如果要填充一个未完成(也就是没有首尾相接)的路径,浏览器将在起、止点件绘制一条直线,然后直接填充。</li>\n</ul>\n</li>\n</ol>\n<p>示例现在应该跟下图一致:</p>\n<iframe width=\"100%\" height=\"200\" src=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/3_canvas_paths/index.html\" loading=\"lazy\"></iframe>\n<div class=\"notecard note\">\n<p><strong>备注:</strong>\n完整代码可到 GitHub 下载:<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/3_canvas_paths\" class=\"external\" target=\"_blank\">3_canvas_paths</a>。</p>\n</div>\n<div class=\"notecard note\">\n<p><strong>备注:</strong>\n请访问我们的<a href=\"/zh-CN/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes\">用画布绘图</a>入门课程来学习更多高级的路径绘制特征,比如贝叶斯曲线。</p>\n</div>"}},{"type":"prose","value":{"id":"文本","title":"文本","isH3":true,"content":"<p>画布可用于绘制文本。我们简要学习一下。首先再次下载一份新的画布模板(<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template\" class=\"external\" target=\"_blank\">1_canvas_template</a>),我们用它来绘制新的示例。</p>\n<p>以下两个函数用于绘制文本:</p>\n<ul>\n<li><a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/fillText\" title=\"fillText()\"><code>fillText()</code></a>:绘制有填充色的文本。</li>\n<li><a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/strokeText\" title=\"strokeText()\"><code>strokeText()</code></a>:绘制文本外边框(描边)。</li>\n</ul>\n<p>这两个函数有三个基本的参数:需要绘制的文字字符串以及绘制起始点的 X、Y 坐标。以<strong>文本框</strong>(即包裹绘制文本的框体)的<strong>左下角</strong>作为起始点。这可能会让你感到困惑,因为其他绘制操作都是以左上角作起始点,留意这一点。</p>\n<p>还有一系列帮助控制文本渲染的属性:比如用于指定字体族、字号的 <a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/font\" title=\"font\"><code>font</code></a>,它的值和语法与 CSS 的 <a href=\"/zh-CN/docs/Web/CSS/font\"><code>font</code></a> 属性一致。</p>\n<p>屏幕阅读器无法获取画布内容。DOM 无法获取在画布绘制的文本,但我们仍要支持 DOM 获取文本。在本例中,我们将文本作为值赋给 <code>aria-label</code>。</p>\n<p>在 JavaScript 代码底部添加以下内容:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.strokeStyle = \"white\";\nctx.lineWidth = 1;\nctx.font = \"36px arial\";\nctx.strokeText(\"Canvas text\", 50, 50);\n\nctx.fillStyle = \"red\";\nctx.font = \"48px georgia\";\nctx.fillText(\"Canvas text\", 50, 150);\n\ncanvas.setAttribute(\"aria-label\", \"Canvas text\");\n</code></pre></div>\n<p>将绘制两行文字,一行描边文字一行填充颜色的文字。示例最终如下所示:</p>\n<iframe width=\"100%\" height=\"180\" src=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/4_canvas_text/index.html\" loading=\"lazy\"></iframe>\n<div class=\"notecard note\">\n<p><strong>备注:</strong>\n完整代码可到 GitHub 下载:<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/4_canvas_text\" class=\"external\" target=\"_blank\">4_canvas_text</a>。</p>\n</div>\n<p>可以自己尝试一下。访问<a href=\"/zh-CN/docs/Web/API/Canvas_API/Tutorial/Drawing_text\">绘制文本</a>获得关于画布文本选项的更多信息。</p>"}},{"type":"prose","value":{"id":"在画布上绘制图片","title":"在画布上绘制图片","isH3":true,"content":"<p>可在画布上渲染外部图片,简单图片文件、视频帧、其他画布内容都可以。这里我们只考虑简单图片文件的情况:</p>\n<ol>\n<li>\n<p>同上,再次拷贝画布模板(<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template\" class=\"external\" target=\"_blank\">1_canvas_template</a>)以绘制新的示例。</p>\n<p><a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/drawImage\" title=\"drawImage()\"><code>drawImage()</code></a> 方法可将图片绘制在画布上。最简单的版本需要三个参数:需要渲染的图片、图片左上角的 X、Y 坐标。</p>\n</li>\n<li>\n<p>首先将图片源嵌入画布中。将以下几行添加到 JavaScript 的底部:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const image = new Image();\nimage.src = \"firefox.png\";\n</code></pre></div>\n<p>这里使用 <a href=\"/zh-CN/docs/Web/API/HTMLImageElement/Image\" title=\"Image()\"><code>Image()</code></a> 构造函数创建了一个新的 <a href=\"/zh-CN/docs/Web/API/HTMLImageElement\"><code>HTMLImageElement</code></a> 对象。返回对象的类型与非空 <a href=\"/zh-CN/docs/Web/HTML/Element/img\"><code>&lt;img&gt;</code></a> 元素的引用是一致的。然后将它的 <a href=\"/zh-CN/docs/Web/HTML/Element/img#src\"><code>src</code></a> 属性设置为 Firefox 的图标。此时浏览器将开始载入这张图片。</p>\n</li>\n<li>\n<p>这次我们尝试用 <code>drawImage()</code> 函数来嵌入图片,应确保图片先载入完毕,否则运行会出错。可以通过 <code>load</code> 事件处理器来达成,该函数只在图片调用完毕后才会调用。在上文代码末尾添加以下内容:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>image.addEventListener(\"load\", () =&gt; ctx.drawImage(image, 20, 20));\n</code></pre></div>\n<p>保存刷新,可以看到图片成功嵌入画布中。</p>\n</li>\n<li>\n<p>还有更多方式。如果仅需要显示图片的某一部分,或者需要改变尺寸,该怎么做呢?复杂版本的 <code>drawImage()</code> 可解决这两个问题。请将 <code>ctx.drawImage()</code> 这一行更新为:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.drawImage(image, 20, 20, 185, 175, 50, 50, 185, 175);\n</code></pre></div>\n<ul>\n<li>第一个参数不变,为图片引用。</li>\n<li>参数 2、3 表示裁切部分左上顶点的坐标,参考原点为原图片本身左上角的坐标。原图片在该坐标左、上的部分均不会绘制出来。</li>\n<li>参数 4、5 表示我们希望从加载的原始图像中裁切出的部分的宽和高。</li>\n<li>参数 6、7 表示裁切部分左上顶点在画布中的位置坐标,参考原点为画布左上顶点。</li>\n<li>参数 8、9 表示裁切部分在画布中绘制的宽和高。本例中绘制时与裁切时面积相同,但你也可以指定不同的值来调整其大小。</li>\n</ul>\n</li>\n<li>\n<p>当图片完全更新后,<a href=\"/zh-CN/docs/Glossary/Accessible_description\">无障碍描述</a>也要跟着更新。</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>canvas.setAttribute(\"aria-label\", \"Firefox Logo\");\n</code></pre></div>\n</li>\n</ol>\n<p>最终结果如下所示:</p>\n<iframe width=\"100%\" height=\"260\" src=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/5_canvas_images/index.html\" loading=\"lazy\"></iframe>\n<div class=\"notecard note\">\n<p><strong>备注:</strong>\n完整代码可到 GitHub 下载:<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/5_canvas_images\" class=\"external\" target=\"_blank\">5_canvas_images</a>。</p>\n</div>"}},{"type":"prose","value":{"id":"循环和动画","title":"循环和动画","isH3":false,"content":"<p>目前我们学习了关于 2D 画布一些非常基础的用法,但是不学习动画你就无法体会画布的强大。画布是提供可编程图形的。如果你的作品不需要改变,那么你就只能永远面对那些静态图片了。</p>"}},{"type":"prose","value":{"id":"创建循环","title":"创建循环","isH3":true,"content":"<p>在画布中使用循环是件有趣的事,你可以在 <a href=\"/zh-CN/docs/Web/JavaScript/Reference/Statements/for\"><code>for</code></a> 循环中运行画布命令,和其他 JavaScript 代码一样。</p>\n<p>我们来创建一个简单的示例。</p>\n<ol>\n<li>\n<p>继续拷贝一份画布模板(<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template\" class=\"external\" target=\"_blank\">1_canvas_template</a>)在代码编辑器中打开。</p>\n</li>\n<li>\n<p>在 JavaScript 代码末尾添加以下一行。这将创建一个新方法——<a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/translate\" title=\"translate()\"><code>translate()</code></a>,可用于移动画布的原点。</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.translate(width / 2, height / 2);\n</code></pre></div>\n<p>这会使原点 (0, 0) 从画布左上顶点移动至画布正中心。这个功能在许多场合非常实用,就像本示例,我们的绘制操作都是围绕着画布的中心点展开的。</p>\n</li>\n<li>\n<p>在 JavaScript 代码末尾添加以下内容:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function degToRad(degrees) {\n return (degrees * Math.PI) / 180;\n}\n\nfunction rand(min, max) {\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\nlet length = 250;\nlet moveOffset = 20;\n\nfor (let i = 0; i &lt; length; i++) {}\n</code></pre></div>\n<p>这里我们实现了一个与上文三角形示例中相同的 <code>degToRad()</code> 函数、一个返回给定范围内随机数 <code>rand()</code> 函数、<code>length</code> 和 <code>moveOffset</code> 变量(见下文),以及一个空的 <code>for</code> 循环。</p>\n</li>\n<li>\n<p>此处的理念是利用 <code>for</code> 循环在画布上循环迭代绘制好玩儿的内容。请将以下代码添加进 <code>for</code> 循环中:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillStyle =\n \"rgba(\" + (255 - length) + \", 0, \" + (255 - length) + \", 0.9)\";\nctx.beginPath();\nctx.moveTo(moveOffset, moveOffset);\nctx.lineTo(moveOffset + length, moveOffset);\nconst triHeight = (length / 2) * Math.tan(degToRad(60));\nctx.lineTo(moveOffset + length / 2, moveOffset + triHeight);\nctx.lineTo(moveOffset, moveOffset);\nctx.fill();\n\nlength--;\nmoveOffset += 0.7;\nctx.rotate(degToRad(5));\n</code></pre></div>\n<p>在每次迭代中:</p>\n<ul>\n<li>设置 <code>fillStyle</code> 为略透明的紫色渐变色。渐变由每次迭代时 <code>length</code> 值的改变实现。随着循环的运行,<code>length</code> 值逐渐变小,从而使连续的三角形颜色逐渐变亮。</li>\n<li>开始路径。</li>\n<li>将笔移动至坐标 <code>(moveOffset, moveOffset)</code>;该变量定义了每次要绘制新三角形时需要移动的距离。</li>\n<li>画一条直线,终点坐标为 <code>(moveOffset+length, moveOffset)</code>。即一条长度为 <code>length</code> 与 X 轴平行的线。</li>\n<li>计算三角形的高,方法同上。</li>\n<li>向三角形底部顶点方向绘制一条直线,然后向三角形的起始点绘制一条直线。</li>\n<li>调用 <code>fill()</code> 为三角形填充颜色。</li>\n<li>更新次序变量,准备绘制下一个三角形。<code>length</code> 的值减一,使三角形每次迭代都变小一些;小幅增加 <code>moveOffset</code> 的值,使得下一个三角形略微错位;用一个新函数 <a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/rotate\" title=\"rotate()\"><code>rotate()</code></a> 来旋转整块画布,在绘制下个三角形前画布旋转 5°。</li>\n</ul>\n</li>\n</ol>\n<p>好了,最终结果如下:</p>\n<iframe width=\"100%\" height=\"550\" src=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/loops_animation/6_canvas_for_loop/index.html\" loading=\"lazy\"></iframe>\n<p>现在,你可以尝试这个示例,可以加一些创新哦。比如:</p>\n<ul>\n<li>把三角形换成矩形、弧,甚至内嵌的图片。</li>\n<li>修改 <code>length</code> 和 <code>moveOffset</code> 的值。</li>\n<li>我们引入了 <code>rand()</code> 函数但是没有使用,你可以试着用它引入一些随机数。</li>\n</ul>\n<div class=\"notecard note\">\n<p><strong>备注:</strong>\n完整代码可到 GitHub 下载:<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/loops_animation/6_canvas_for_loop\" class=\"external\" target=\"_blank\">6_canvas_for_loop</a>。</p>\n</div>"}},{"type":"prose","value":{"id":"动画","title":"动画","isH3":true,"content":"<p>上文的循环示例很有趣,其实在重度画布应用(比如游戏或实时可视化)中恒定循环是至关重要的支持组件。如果期望画布显示的内容像一部电影,屏幕最好能够以 60 帧每秒的刷新率实时更新,这样人眼看到的动作才更真实、更平滑。</p>\n<p>一些 JavaScript 函数可以让函数在一秒内重复运行多次,这里最适合的就是 <a href=\"/zh-CN/docs/Web/API/Window/requestAnimationFrame\"><code>window.requestAnimationFrame()</code></a>。它只取一个参数,即每帧要运行的函数名。下一次浏览器准备好更新屏幕时,将会调用你的函数。如果你的函数向动画中绘制了更新内容,则在函数结束前再次调用 <code>requestAnimationFrame()</code>,动画循环得以保留。只有在停止调用 <code>requestAnimationFrame()</code> 时,或 <code>requestAnimationFrame()</code> 调用后、帧调用前调用了 <a href=\"/zh-CN/docs/Web/API/Window/cancelAnimationFrame\"><code>window.cancelAnimationFrame()</code></a> 时,循环才会停止。</p>\n<div class=\"notecard note\">\n<p><strong>备注:</strong>\n动画结束后在主代码中调用 <code>cancelAnimationFrame()</code> 是良好习惯,可以确保不再有等待运行的更新。</p>\n</div>\n<p>浏览器自行处理诸如“使动画匀速运行”、“避免在不可见的内容浪费资源”等复杂细节问题。</p>\n<p>我们简单回顾一下“弹球”示例(<a href=\"https://mdn.github.io/learning-area/javascript/oojs/bouncing-balls/index-finished.html\" class=\"external\" target=\"_blank\">在线运行</a>或查看<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/oojs/bouncing-balls\" class=\"external\" target=\"_blank\">源代码</a>),来探究一下原理。以下是让弹球持续运行的循环代码:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function loop() {\n ctx.fillStyle = \"rgb(0 0 0 / 25%)\";\n ctx.fillRect(0, 0, width, height);\n\n for (const ball of balls) {\n ball.draw();\n ball.update();\n ball.collisionDetect();\n }\n\n requestAnimationFrame(loop);\n}\n\nloop();\n</code></pre></div>\n<p>我们在代码底部运行了一次 <code>loop()</code> 函数,它启动了整个循环,绘制了第一帧动画。接着 <code>loop()</code> 函数接管了 <code>requestAnimationFrame(loop)</code> 的调用工作,运行下一帧、再下一帧和之后的动画。</p>\n<p>请注意每一帧我们都完全清空画布并重新渲染所有内容。(每帧创建一个新球(25 个封顶),然后绘制每个球,更新它们的位置,检查是否撞到了其他球。)向画布中绘制的新图形不能像 DOM 元素那样单独操作。你无法再画布中单独操作某一个球,因为只要绘制完毕了,它就是画布的一部分,而不是一个单独的球。你需要擦除再重画,可以将整帧擦除再重画整个画面,也可通过编程选择最小的部分进行擦除和重画。</p>\n<p>优化图形动画是另一个编程主题,需要好多奇技淫巧。这超出我们的讨论范围啦。</p>\n<p>一般地,在画布上制作动画需要以下步骤:</p>\n<ol>\n<li>清除画布内容(可用 <a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/fillRect\" title=\"fillRect()\"><code>fillRect()</code></a> 或 <a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/clearRect\" title=\"clearRect()\"><code>clearRect()</code></a>)。</li>\n<li>(在需要时)用 <a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/save\" title=\"save()\"><code>save()</code></a> 保存状态。(在进行下一步前保存所更新的设置,一般在复杂环境中用到)</li>\n<li>绘制动画图形。</li>\n<li>使用 <a href=\"/zh-CN/docs/Web/API/CanvasRenderingContext2D/restore\" title=\"restore()\"><code>restore()</code></a> 恢复第 2 步中保存的状态。</li>\n<li>调用 <code>requestAnimationFrame()</code> 准备下一帧动画。</li>\n</ol>\n<div class=\"notecard note\">\n<p><strong>备注:</strong><code>save()</code> 和 <code>restore()</code> 这里暂不展开,可以访问<a href=\"/zh-CN/docs/Web/API/Canvas_API/Tutorial/Transformations\">变形</a>教程(及后续内容)来获取详细信息。</p>\n</div>"}},{"type":"prose","value":{"id":"一个简单的人物动画","title":"一个简单的人物动画","isH3":true,"content":"<p>现在我们来创建一个简单的动画,我们找来一个复古的电脑游戏的主角制作一个在屏幕上行走的动画。</p>\n<ol>\n<li>\n<p>继续拷贝一份画布模板(<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template\" class=\"external\" target=\"_blank\">1_canvas_template</a>)在代码编辑器中打开。</p>\n</li>\n<li>\n<p>更新内部 HTML 以展示图片:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">html</span></div><pre class=\"brush: html notranslate\"><code>&lt;canvas class=\"myCanvas\"&gt;\n &lt;p&gt;A man walking.&lt;/p&gt;\n&lt;/canvas&gt;\n</code></pre></div>\n</li>\n<li>\n<p>在 JS 代码末尾添加下面一行,再次将画布的原点设置为中心点。</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.translate(width / 2, height / 2);\n</code></pre></div>\n</li>\n<li>\n<p>创建一个新的 <a href=\"/zh-CN/docs/Web/API/HTMLImageElement\"><code>HTMLImageElement</code></a> 对象,把它的 <a href=\"/zh-CN/docs/Web/HTML/Element/img#src\"><code>src</code></a> 设置为所需图片,添加一个 <code>onload</code> 事件处理器,使 <code>draw()</code> 函数在图片载入后触发。</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const image = new Image();\nimage.src = \"walk-right.png\";\nimage.onload = draw;\n</code></pre></div>\n</li>\n<li>\n<p>添加一些变量,来追踪精灵图在屏幕上的位置,以及当前需要显示的精灵图的序号。</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>let sprite = 0;\nlet posX = 0;\n</code></pre></div>\n<p>我们来解释一下“精灵图序列”(我们借鉴了麦克托马斯的<a href=\"https://codepen.io/mikethomas/pen/kQjKLW\" class=\"external\" target=\"_blank\">使用 CSS 动画创建人物行走的精灵图</a>)。图片如下:</p>\n<p><img src=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics/walk-right.png\" alt=\"包含六个精灵的精灵图,模拟从右侧观察一个像素化人物向前走时的不同瞬间\" width=\"612\" height=\"148\" loading=\"lazy\"></p>\n<p>图中包含六个精灵,它们组成了一趟完整的行走序列。每个精灵的尺寸为 102 × 148 像素。为了整齐的显示一个精灵,可以通过 <code>drawImage()</code> 来从序列中裁切出单独的精灵并隐藏其他部分,就像上文中操作 Firefox 图标的方法。切片的 X 坐标应为 102 的倍数,Y 坐标恒为 0。切片尺寸恒为 102 × 148 像素。</p>\n</li>\n<li>\n<p>在代码末尾添加一个空的 <code>draw()</code> 函数,用来添加一些代码:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function draw() {}\n</code></pre></div>\n</li>\n<li>\n<p>本部分的剩余代码都在这个 <code>draw()</code> 中展开。首先,添加以下代码,清除画布,准备绘制新的帧。注意由于我们刚才将原点设置为 <code>width/2, height/2</code>,这里需要将矩形左上顶点的坐标设置为 <code>-(width/2), -(height/2)</code>。</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.fillRect(-(width / 2), -(height / 2), width, height);\n</code></pre></div>\n</li>\n<li>\n<p>下一步,我们使用 <code>drawImage()</code>(9 参数版本)来绘制图形,添加以下代码:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>ctx.drawImage(image, sprite * 102, 0, 102, 148, 0 + posX, -74, 102, 148);\n</code></pre></div>\n<p>如你所见:</p>\n<ul>\n<li><code>image</code> 指定需要嵌入的图片。</li>\n<li>参数 2、3 指定切片左上顶点在原图的位置坐标,X 值为 <code>sprite</code>(精灵序列 0—5)乘 102,Y 值恒为 0。</li>\n<li>参数 4、5 指定切片尺寸:102 × 148 像素。</li>\n<li>参数 6、7 指定切片在画布绘制区域的坐上顶点坐标。X 坐标位置为 0 + <code>posX</code>,意味着我们可以通过修改 <code>posX</code> 的值来修改绘制的位置。</li>\n<li>参数 8、9 指定图片在画布中的尺寸。这里需要图片保持原始尺寸,因此我们指定宽、高值为 102、148。</li>\n</ul>\n</li>\n<li>\n<p>现在,我们在每帧绘制完毕(部分完毕)后修改 <code>sprite</code> 的值。在 <code>draw()</code> 函数底部添加以下内容:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>if (posX % 13 === 0) {\n if (sprite === 5) {\n sprite = 0;\n } else {\n sprite++;\n }\n}\n</code></pre></div>\n<p>将整个功能块放置在 <code>if (posX % 13 === 0) { ... }</code> 内。用“模(<code>%</code>)运算符”(即<a href=\"/zh-CN/docs/Web/JavaScript/Reference/Operators/Remainder\">求余运算符</a>)来检测 <code>posX</code> 是否可以被 13 整除。如果整除,则通过增加 <code>sprite</code> 的值转至下一个精灵(到 5 号精灵时归零)。这实际上意味着每隔 13 帧才更新一次精灵,每秒大约更新 5 帧(<code>requestAnimationFrame()</code> 每秒最多调用 60 帧)。我们故意放慢了帧率,因为精灵图只有六个,且如果每秒显示 60 帧的话,这个角色就会快到起飞。</p>\n<p>外部程序块中用一个 <a href=\"/zh-CN/docs/Web/JavaScript/Reference/Statements/if...else\"><code>if...else</code></a> 语句来检测 <code>sprite</code> 的值是否为 5(精灵序号在 0—5 间循环,因此 5 代表最后一个精灵)。如果最后一个精灵已经显示,就把 <code>sprite</code> 重置为 0,否则加 1。</p>\n</li>\n<li>\n<p>下一步要算出每帧 <code>posX</code> 的值,在上文代码末尾添加以下内容:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>if (posX &gt; width / 2) {\n let newStartPos = -(width / 2 + 102);\n posX = Math.ceil(newStartPos);\n console.log(posX);\n} else {\n posX += 2;\n}\n</code></pre></div>\n<p>用另一个 <code>if...else</code> 来检测 <code>posX</code> 的值是否超出了 <code>width/2</code>,那意味着角色走到了屏幕右侧边缘。如果角色没有走到屏幕边缘,只需为 <code>posX</code> 加 2。这将让它在下次绘制时更靠右些。</p>\n</li>\n<li>\n<p>最后,通过在 <code>draw()</code> 函数末尾添加 <a href=\"/zh-CN/docs/Web/API/Window/requestAnimationFrame\" title=\"requestAnimationFrame()\"><code>requestAnimationFrame()</code></a> 调用以实现动画的循环。</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>window.requestAnimationFrame(draw);\n</code></pre></div>\n</li>\n</ol>\n<p>成功了!最终效果如下所示:</p>\n<iframe width=\"100%\" height=\"260\" src=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation/index.html\" loading=\"lazy\"></iframe>\n<div class=\"notecard note\">\n<p><strong>备注:</strong>\n完整代码可到 GitHub 下载:<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation\" class=\"external\" target=\"_blank\">7_canvas_walking_animation</a>。</p>\n</div>"}},{"type":"prose","value":{"id":"简单的绘图应用","title":"简单的绘图应用","isH3":true,"content":"<p>下面来演示一个简单的绘图应用,作为最后一个绘画示例,它将向你展示动画循环如果与用户输入(本例中为鼠标移动)结合起来。我们不会带你一步一步来实现本示例,只对代码中最有趣的部分进行探究。</p>\n<p>示例代码可到 GitHub 下载:<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/loops_animation/8_canvas_drawing_app\" class=\"external\" target=\"_blank\">8_canvas_drawing_app</a>,也可在线试玩:</p>\n<iframe width=\"100%\" height=\"600\" src=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/loops_animation/8_canvas_drawing_app/index.html\" loading=\"lazy\"></iframe>\n<p>下面我们就来看看代码的精华部分。首先,用 <code>curX</code>、<code>curY</code> 和 <code>pressed</code> 这三个变量来跟踪鼠标的 X、Y 坐标和点击状态。当鼠标移动时,触发一个函数作为 <code>onmousemove</code> 事件处理器,其应捕获当前的 X 和 Y 值。再用 <code>onmousedown</code> 和 <code>onmouseup</code> 事件处理器来修改鼠标键按下时 <code>pressed</code> 的值(按下为 <code>true</code>,释放为 <code>false</code>)。</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>let curX;\nlet curY;\nlet pressed = false;\n\n// 更新鼠标指针坐标\ndocument.addEventListener(\"mousemove\", (e) =&gt; {\n curX = e.pageX;\n curY = e.pageY;\n});\n\ncanvas.addEventListener(\"mousedown\", () =&gt; (pressed = true));\n\ncanvas.addEventListener(\"mouseup\", () =&gt; (pressed = false));\n</code></pre></div>\n<p>在按下“Clear canvas”(清除画布)按钮时,我们运行一个简单的函数来清除整个画布的内容至纯黑色,和刚才的方法一致:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>clearBtn.addEventListener(\"click\", () =&gt; {\n ctx.fillStyle = \"rgb(0 0 0)\";\n ctx.fillRect(0, 0, width, height);\n});\n</code></pre></div>\n<p>这次的绘图循环非常简单,如果 <code>pressed</code> 为 <code>true</code>,则绘制一个圆,该圆以颜色选择器中设定的颜色为背景,以滑动选择器设定的数值为半径。我们必须在测量位置上方 85 像素处画圆圈,因为垂直测量是从视口顶部开始的,而我们要相对于画布顶部画圆圈(画布顶部是从 85 像素高的工具栏下方算起)。如果我们只使用 <code>curY</code> 作为 Y 坐标来绘制,那么绘制出来的圆圈会比鼠标位置低 85 个像素。</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function draw() {\n if (pressed) {\n ctx.fillStyle = colorPicker.value;\n ctx.beginPath();\n ctx.arc(\n curX,\n curY - 85,\n sizePicker.value,\n degToRad(0),\n degToRad(360),\n false,\n );\n ctx.fill();\n }\n\n requestAnimationFrame(draw);\n}\n\ndraw();\n</code></pre></div>\n<p>浏览器支持所有 <a href=\"/zh-CN/docs/Web/HTML/Element/input\"><code>&lt;input&gt;</code></a> 类型。如果某输入类型不被浏览器支持,那么它将被降格为纯文本输入。</p>"}},{"type":"prose","value":{"id":"webgl","title":"WebGL","isH3":false,"content":"<p>2D 内容告一段落,现在简单了解一下 3D 画布。3D 画布内容可通过的 <a href=\"/zh-CN/docs/Web/API/WebGL_API\">WebGL</a> API 实现,尽管它和 2D canvas API 都可在 <a href=\"/zh-CN/docs/Web/HTML/Element/canvas\"><code>&lt;canvas&gt;</code></a> 元素上进行渲染,但两者是彼此独立的。</p>\n<p>WebGL 基于 <a href=\"/zh-CN/docs/Glossary/OpenGL\">OpenGL</a>(开放图形库)实现,可直接与 <a href=\"/zh-CN/docs/Glossary/GPU\">GPU</a> 通信,基于此,编写纯 WebGL 代码与常规的 JavaScript 不尽相同,更像 C++ 那样的底层语言,更加复杂,但无比强大。</p>"}},{"type":"prose","value":{"id":"使用库","title":"使用库","isH3":true,"content":"<p>由于 3D 绘图的复杂性,大多数人写代码时会使用第三方 JavaScript 库(比如 <a href=\"/zh-CN/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js\">Three.js</a>、<a href=\"/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_PlayCanvas\" class=\"only-in-en-us\">PlayCanvas</a> 或 <a href=\"/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Babylon.js\" class=\"only-in-en-us\">Babylon.js</a>)。大多数库的原理都基本类似,提供创建基本的、自定义性状的功能、视图定位摄影和光效、表面纹理覆盖,等等。库负责 与 WebGL 通信,你只需完成更高阶工作。</p>\n<p>接触任何一个库都意味着要学一套全新的 API(这里是第三方的版本),但都比使用原生 WebGL 编程简单。</p>"}},{"type":"prose","value":{"id":"重新创建魔方","title":"重新创建魔方","isH3":true,"content":"<p>我们来看一个简单的示例,用一套 WebGL 库(这里我们选择 <a href=\"/zh-CN/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js\">Three.js</a>,最流行的 3D 绘图库之一)来创建我们在本文开头看到的旋转魔方。</p>\n<ol>\n<li>\n<p>首先,新建文件夹并拷贝 <a href=\"https://github.com/mdn/learning-area/blob/main/javascript/apis/drawing-graphics/threejs-cube/index.html\" class=\"external\" target=\"_blank\">threejs-cube/index.html</a> 到本地,然后拷贝 <a href=\"https://github.com/mdn/learning-area/blob/main/javascript/apis/drawing-graphics/threejs-cube/metal003.png\" class=\"external\" target=\"_blank\">metal003.png</a> 到同一文件夹。图片将用作魔方的表面纹理。</p>\n</li>\n<li>\n<p>然后,继续在同一个文件夹内创建 <code>script.js</code> 文件。</p>\n</li>\n<li>\n<p>接下来,你需要安装 Three.js 库。你可参考<a href=\"/zh-CN/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js\">使用 Three.js 构建基础演示</a>中的环境设置步骤,以便让 Three.js 如预期运行。</p>\n</li>\n<li>\n<p>现在 <code>three.js</code> 在页面上可用,我们可以在 <code>script.js</code> 里写 JavaScript 代码了。先从创建一个新场景开始吧——在你的 <code>script.js</code> 文件中添加以下代码:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const scene = new THREE.Scene();\n</code></pre></div>\n<p><a href=\"https://threejs.org/docs/index.html#Reference/Scenes/Scene\" class=\"external\" target=\"_blank\"><code>Scene()</code></a> 构造函数创建一个新的场景,表示即将显示的整个 3D 世界。</p>\n</li>\n<li>\n<p>下一步,我们需要一部<strong>摄影机</strong>来看到整个场景。在 3D 绘图语境中,摄影机表示观察者在世界里的位置,可通过下面代码创建一部摄影机:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const camera = new THREE.PerspectiveCamera(\n 75,\n window.innerWidth / window.innerHeight,\n 0.1,\n 1000,\n);\ncamera.position.z = 5;\n</code></pre></div>\n<p><a href=\"https://threejs.org/docs/index.html#Reference/Cameras/PerspectiveCamera\" class=\"external\" target=\"_blank\"><code>PerspectiveCamera()</code></a> 构造函数有四个参数:</p>\n<ul>\n<li>观察区域:镜头视角大小,用角度表示。</li>\n<li><a href=\"/zh-CN/docs/Glossary/Aspect_ratio\">纵横比</a>:一般情况下,宽高比等于屏幕的宽比上屏幕的高。使用其他的值会使场景扭曲(也许正是你需要的,但一般都不是)。</li>\n<li>近裁切面:停止渲染前对象离摄影机的最近距离。设想一下,举起一个手指,逐渐移近双眼,某个点后就在也看不到这根手指了。</li>\n<li>远裁切面:停止渲染前离摄像机最远的对象的距离。</li>\n</ul>\n<p>将摄像机的位置设定为距 Z 轴 5 个距离单位的位置。与 CSS 类似,在屏幕之外你(观察者)的位置。</p>\n</li>\n<li>\n<p>第三个重要参数是渲染器。我们用它来渲染给定的场景,通过给定的摄影机观察。现在我们使用 <a href=\"https://threejs.org/docs/index.html#Reference/Renderers/WebGLRenderer\" class=\"external\" target=\"_blank\"><code>WebGLRenderer()</code></a> 构造函数创建一个渲染器供稍后使用。添加以下代码:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const renderer = new THREE.WebGLRenderer();\nrenderer.setSize(window.innerWidth, window.innerHeight);\ndocument.body.appendChild(renderer.domElement);\n</code></pre></div>\n<p>第一行创建一个新的渲染器,第二行设定渲染器在当前摄影机视角下的尺寸,第三行将渲染好的 <a href=\"/zh-CN/docs/Web/HTML/Element/canvas\"><code>&lt;canvas&gt;</code></a> 对象加入文档的 <a href=\"/zh-CN/docs/Web/HTML/Element/body\"><code>&lt;body&gt;</code></a> 中。现在渲染器绘制的内容将在窗口中显示出来。</p>\n</li>\n<li>\n<p>下一步,在画布中创建魔方。把以下代码添加到 JavaScript 的底部:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>let cube;\n\nconst loader = new THREE.TextureLoader();\n\nloader.load(\"metal003.png\", (texture) =&gt; {\n texture.wrapS = THREE.RepeatWrapping;\n texture.wrapT = THREE.RepeatWrapping;\n texture.repeat.set(2, 2);\n\n const geometry = new THREE.BoxGeometry(2.4, 2.4, 2.4);\n const material = new THREE.MeshLambertMaterial({ map: texture });\n cube = new THREE.Mesh(geometry, material);\n scene.add(cube);\n\n draw();\n});\n</code></pre></div>\n<p>内容很多,我们来剥丝抽茧:</p>\n<ul>\n<li>首先,创建一个全局变量 <code>cube</code>,这样就可以在代码任意位置访问我们的魔方。</li>\n<li>然后,创建一个 <a href=\"https://threejs.org/docs/index.html#api/zh/loaders/TextureLoader\" class=\"external\" target=\"_blank\"><code>TextureLoader</code></a> 对象,并调用 <code>load()</code>。这里 <code>load()</code> 包含两个参数(其他情况可以有更多参数):需要调用的纹理图(PNG 文件)和纹理加载成功后调用的函数。</li>\n<li>函数内部,我们用 <a href=\"https://threejs.org/docs/index.html#api/zh/textures/Texture\" class=\"external\" target=\"_blank\"><code>texture</code></a> 对象的属性指明我们要在魔方的每个面渲染 2 × 2 的图片,然后创建一个 <a href=\"https://threejs.org/docs/index.html#api/zh/geometries/BoxGeometry\" class=\"external\" target=\"_blank\"><code>BoxGeometry</code></a> 对象和一个 <a href=\"https://threejs.org/docs/index.html#api/en/materials/MeshLambertMaterial\" class=\"external\" target=\"_blank\"><code>MeshLambertMaterial</code></a> 对象,将两者作为 <a href=\"https://threejs.org/docs/index.html#api/zh/objects/Mesh\" class=\"external\" target=\"_blank\"><code>Mesh</code></a> 的参数来创建我们的魔方。<a href=\"https://threejs.org/docs/index.html#api/zh/objects/Mesh\" class=\"external\" target=\"_blank\"><code>Mesh</code></a> 一般就需要两个参数:一个几何(形状)和一个素材(形状表面外观)。</li>\n<li>最后,将魔方添加进场景中,调用我们的 <code>draw()</code> 函数启用动画。</li>\n</ul>\n</li>\n<li>\n<p>定义 <code>draw()</code> 函数前,我们需要先为场景打光,以照亮场景中的物体。请添加以下代码:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>const light = new THREE.AmbientLight(\"rgb(255 255 255)\"); // 柔白光\nscene.add(light);\n\nconst spotLight = new THREE.SpotLight(\"rgb(255 255 255)\");\nspotLight.position.set(100, 1000, 1000);\nspotLight.castShadow = true;\nscene.add(spotLight);\n</code></pre></div>\n<p><a href=\"https://threejs.org/docs/index.html#api/zh/lights/AmbientLight\" class=\"external\" target=\"_blank\"><code>AmbientLight</code></a> 对象是可以轻度照亮整个场景的柔光,就像户外的阳光。而 <a href=\"https://threejs.org/docs/index.html#api/zh/lights/SpotLight\" class=\"external\" target=\"_blank\"><code>SpotLight</code></a> 对象是直射的硬光,就像闪光灯和手电筒(或者它的英文字面意思——聚光灯)。</p>\n</li>\n<li>\n<p>最后,在代码末尾添加我们的 <code>draw()</code> 函数:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">js</span></div><pre class=\"brush: js notranslate\"><code>function draw() {\n cube.rotation.x += 0.01;\n cube.rotation.y += 0.01;\n renderer.render(scene, camera);\n\n requestAnimationFrame(draw);\n}\n</code></pre></div>\n<p>这段代码很直观,每一帧我们都沿 X 轴 和 Y 轴将魔方轻微转动,然后按摄像机视角渲染场景,最后调用 <code>requestAnimationFrame()</code> 来准备下一帧。</p>\n</li>\n</ol>\n<p>回顾一下最终效果:</p>\n<iframe width=\"100%\" height=\"500\" src=\"https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/threejs-cube/index.html\" loading=\"lazy\"></iframe>\n<p>你可以<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/threejs-cube\" class=\"external\" target=\"_blank\">到 Github 下载完成的代码</a>。</p>\n<div class=\"notecard note\">\n<p><strong>备注:</strong>\n在我们的 GitHub 仓库还有另一个趣味 3D 魔方示例——<a href=\"https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/threejs-video-cube\" class=\"external\" target=\"_blank\">Three.js Video Cube</a>(在线查看)。其中通过 <a href=\"/zh-CN/docs/Web/API/MediaDevices/getUserMedia\" title=\"getUserMedia()\"><code>getUserMedia()</code></a> 来从电脑摄像头获取一段视频,将其投影到魔方上作为纹理。</p>\n</div>"}},{"type":"prose","value":{"id":"总结","title":"总结","isH3":false,"content":"<p>此刻你以经了解了一些 Canvas 和 WebGL 图形编程的基本理念和简单应用,你一定产生了不少创作灵感,玩得开心!</p>"}},{"type":"prose","value":{"id":"参见","title":"参见","isH3":false,"content":"<p>本文我们只涉及到画布最为基本的内容,以下内容帮你探索更多:</p>\n<ul>\n<li><a href=\"/zh-CN/docs/Web/API/Canvas_API/Tutorial\">Canvas 教程</a>:一个详尽的教程系列,更细致深入地讲解了 2D 画布所需的知识。必读。</li>\n<li><a href=\"/zh-CN/docs/Web/API/WebGL_API/Tutorial\">WebGL 教程</a>:纯 WebGL 编程教程系列。</li>\n<li><a href=\"/zh-CN/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js\">用 Three.js 创建一个简单的示例</a>:Three.js 基础教程。我们还提供 <a href=\"/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_PlayCanvas\" class=\"only-in-en-us\">PlayCanvas</a> 和 <a href=\"/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Babylon.js\" class=\"only-in-en-us\">Babylon.js</a> 的基础教程。</li>\n<li><a href=\"/zh-CN/docs/Games\">游戏开发</a>:MDN Web 游戏开发目录页。提供与 2D、3D 画布相关的实用教程和技术,可参考“技术”和“教程”菜单项。</li>\n</ul>"}},{"type":"prose","value":{"id":"示例","title":"示例","isH3":false,"content":"<ul>\n<li><a href=\"https://github.com/mdn/violent-theremin\" class=\"external\" target=\"_blank\">Violent theramin</a>:用 Web 音频 API 创建声音,用画布显示漂亮的视觉效果以配合音乐。</li>\n<li><a href=\"https://github.com/mdn/voice-change-o-matic\" class=\"external\" target=\"_blank\">Voice change-o-matic</a>:用画布为 Web 音频 API 产生的音效提供实时的视觉效果。</li>\n</ul>\n<ul class=\"prev-next\"><li><a class=\"button secondary\" href=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Video_and_audio_APIs\"><span class=\"button-wrap\"> 上一页 </span></a></li><li><a class=\"button secondary\" href=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs\"><span class=\"button-wrap\"> 概述:客户端 Web API</span></a></li><li><a class=\"button secondary\" href=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Client-side_storage\"><span class=\"button-wrap\"> 下一页 </span></a></li></ul>"}}],"isActive":true,"isMarkdown":true,"isTranslated":true,"locale":"zh-CN","mdn_url":"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics","modified":"2024-12-21T13:03:41.000Z","native":"中文 (简体)","noIndexing":false,"other_translations":[{"locale":"en-US","title":"Drawing graphics","native":"English (US)"},{"locale":"de","title":"Zeichnen von Grafiken","native":"Deutsch"},{"locale":"fr","title":"Dessiner des graphismes","native":"Français"},{"locale":"ja","title":"グラフィックの描画","native":"日本語"}],"pageTitle":"绘图 - 学习 Web 开发 | MDN","parents":[{"uri":"/zh-CN/docs/Learn_web_development","title":"学习 Web 开发"},{"uri":"/en-US/docs/Learn_web_development/Extensions","title":"Extension modules"},{"uri":"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs","title":"客户端 Web API"},{"uri":"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics","title":"绘图"}],"popularity":null,"short_title":"绘图","sidebarHTML":"<ol><li class=\"section\"><a href=\"/zh-CN/docs/Learn_web_development/Getting_started\">入门模块</a></li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Getting_started/Environment_setup\">配置环境</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Getting_started/Environment_setup/Installing_software\">安装基础软件</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Getting_started/Environment_setup/Browsing_the_web\">网页、网站、网络服务器和搜索引擎的区别是什么?</a></li><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Environment_setup/Code_editors\" class=\"only-in-en-us\">Code editors</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Getting_started/Environment_setup/Dealing_with_files\">处理文件</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Getting_started/Environment_setup/Command_line\">命令行速成课</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Getting_started/Your_first_website\">你的第一个网站</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Getting_started/Your_first_website/What_will_your_website_look_like\">你的网站会是什么样子?</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Getting_started/Your_first_website/Creating_the_content\">HTML 基础</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Getting_started/Your_first_website/Styling_the_content\">CSS 基础</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Getting_started/Your_first_website/Adding_interactivity\">JavaScript 基础</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Getting_started/Your_first_website/Publishing_your_website\">发布你的网站</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Getting_started/Web_standards\" class=\"only-in-en-us\">Web 标准</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Getting_started/Web_standards/How_the_web_works\">万维网是如何工作的</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Getting_started/Web_standards/The_web_standards_model\">Web 和 Web 标准</a></li><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Web_standards/How_browsers_load_websites\" class=\"only-in-en-us\">How browsers load websites</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/en-US/docs/Learn_web_development/Getting_started/Soft_skills\" class=\"only-in-en-us\">软性技能</a></summary><ol><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Soft_skills/Research_and_learning\" class=\"only-in-en-us\">Research and learning</a></li><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Soft_skills/Collaboration_and_teamwork\" class=\"only-in-en-us\">Collaboration and teamwork</a></li><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Soft_skills/Workflows_and_processes\" class=\"only-in-en-us\">Workflows and processes</a></li><li><a href=\"/en-US/docs/Learn_web_development/Getting_started/Soft_skills/Job_interviews\" class=\"only-in-en-us\">Succeeding in job interviews</a></li></ol></details></li><li class=\"section\"><a href=\"/en-US/docs/Learn_web_development/Core\" class=\"only-in-en-us\">核心模块</a></li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Core/Structuring_content\">使用 HTML 构建内容</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Structuring_content/Basic_HTML_syntax\">开始学习 HTML</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Structuring_content/Webpage_metadata\">“头”里有什么——HTML 元信息</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Structuring_content/Headings_and_paragraphs\">HTML 文本处理基础</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Structuring_content/Emphasis_and_importance\">强调与重要性</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/Lists\" class=\"only-in-en-us\">Lists</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Structuring_content/Structuring_documents\">文档与网站架构</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Structuring_content/Advanced_text_features\">文本格式进阶</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Structuring_content/Creating_links\">创建超链接</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Structuring_content/Marking_up_a_letter\">标记信件</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Structuring_content/Structuring_a_page_of_content\">构建网页内容</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Structuring_content/HTML_images\">HTML 中的图片</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Structuring_content/HTML_video_and_audio\">视频和音频内容</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Structuring_content/Mozilla_splash_page\">Mozilla 欢迎页面</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Structuring_content/HTML_table_basics\">HTML 表格基础</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Structuring_content/Table_accessibility\">HTML 表格进阶特性和无障碍</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Structuring_content/Planet_data_table\">作业:构建行星数据</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Structuring_content/HTML_forms\" class=\"only-in-en-us\">Forms and buttons in HTML</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Structuring_content/Debugging_HTML\">HTML 调试</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics\">CSS 样式基础</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/What_is_CSS\">CSS 如何运行</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Getting_started\">让我们开始 CSS 的学习之旅</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Styling_a_bio_page\">运用你的新知识</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Basic_selectors\">CSS 选择器</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Attribute_selectors\">属性选择器</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Pseudo_classes_and_elements\">伪类和伪元素</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Combinators\">关系选择器</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Box_model\">盒模型</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Handling_conflicts\">层叠、优先级与继承</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Values_and_units\">CSS 值和单位</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Sizing\">在 CSS 中调整大小</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Backgrounds_and_borders\">背景与边框</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Overflow\">溢出的内容</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Images_media_forms\">图像、媒体和表单元素</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Tables\">样式化表格</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Debugging_CSS\">调试 CSS</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Fundamental_CSS_comprehension\">基本的 CSS 理解</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Fancy_letterheaded_paper\">创建精美的信纸</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Styling_basics/Cool-looking_box\">一个漂亮的盒子</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Core/Text_styling\">CSS 文本样式</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Text_styling/Fundamentals\">基本文本和字体样式</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Text_styling/Styling_lists\">为列表添加样式</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Text_styling/Styling_links\">样式化链接</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Text_styling/Web_fonts\">Web 字体</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Text_styling/Typesetting_a_homepage\">作业:排版社区大学首页</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Core/CSS_layout\">CSS 排版</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Core/CSS_layout/Introduction\">介绍 CSS 布局</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/CSS_layout/Floats\">浮动</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/CSS_layout/Positioning\">定位</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/CSS_layout/Flexbox\">弹性盒子</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/CSS_layout/Grids\">网格</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/CSS_layout/Responsive_Design\">响应式设计</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/CSS_layout/Media_queries\">媒体查询入门指南</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/CSS_layout/Fundamental_Layout_Comprehension\">作业:基本布局理解</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting\">使用 JavaScript 动态编码</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/What_is_JavaScript\">什么是 JavaScript?</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/A_first_splash\">JavaScript 初体验</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/What_went_wrong\">查找并解决 JavaScript 代码的错误</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/Variables\">如何存储你需要的信息——变量</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/Math\">JavaScript 中的基础数学 — 数字和操作符</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/Strings\">文本处理——JavaScript 中的字符串</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/Useful_string_methods\">有用的字符串方法</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/Arrays\">数组</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/Silly_story_generator\">笑话生成器</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/Conditionals\">在代码中做决定——条件语句</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/Loops\">循环吧,代码</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/Functions\">函数——可复用的代码块</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/Build_your_own_function\">创建你自己的函数</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/Return_values\">函数返回值</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/Events\">事件介绍</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/Event_bubbling\">事件冒泡</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/Image_gallery\">图片库</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/Object_basics\">JavaScript 对象基础</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/DOM_scripting\">DOM 脚本简介</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/Network_requests\">从服务器获取数据</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/JSON\">使用 JSON</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Scripting/Debugging_JavaScript\">处理常见的 JavaScript 问题</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Core/Frameworks_libraries\">JavaScript 架构和库</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Frameworks_libraries/Introduction\">客户端框架介绍</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Frameworks_libraries/Main_features\">框架的主要特性</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Frameworks_libraries/React_getting_started\">React 入门</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Frameworks_libraries/React_todo_list_beginning\">开始我们的 React 待办清单</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Frameworks_libraries/React_components\">组件化我们的 React App</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_interactivity_events_state\" class=\"only-in-en-us\">React interactivity: Events and state</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_interactivity_filtering_conditional_rendering\" class=\"only-in-en-us\">React interactivity: Editing, filtering, conditional rendering</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_accessibility\" class=\"only-in-en-us\">Accessibility in React</a></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Frameworks_libraries/React_resources\" class=\"only-in-en-us\">React resources</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Core/Accessibility\">无障碍</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Accessibility/What_is_accessibility\">什么是无障碍?</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Accessibility/Tooling\">解决常见的无障碍问题</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Accessibility/HTML\">HTML:无障碍的良好基础</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Accessibility/CSS_and_JavaScript\">CSS 和 JavaScript 无障碍最佳实践</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Accessibility/WAI-ARIA_basics\">WAI-ARIA 基础</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Accessibility/Multimedia\">多媒体无障碍</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Accessibility/Mobile\">移动端无障碍</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Accessibility/Accessibility_troubleshooting\">测验:无障碍疑难解答</a></li></ol></details></li><li><a href=\"/en-US/docs/Learn_web_development/Core/Design_for_developers\" class=\"only-in-en-us\">为开发人员设计</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Core/Version_control\">版本控制</a></li><li class=\"section\"><a href=\"/en-US/docs/Learn_web_development/Extensions\" class=\"only-in-en-us\">扩展模块</a></li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects\">Advanced JavaScript objects</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_prototypes\">对象原型</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object-oriented_programming\">面向对象编程基本概念</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Classes_in_JavaScript\">JavaScript 中的类</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_building_practice\">实践对象构造</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Adding_bouncing_balls_features\">为“弹球”示例添加新功能</a></li></ol></details></li><li class=\"toggle\"><details open=\"\"><summary><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs\">客户端 Web API</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Introduction\">Web API 简介</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Video_and_audio_APIs\">视频和音频 API</a></li><li><em><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Drawing_graphics\" aria-current=\"page\">绘图</a></em></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Client-side_storage\">客户端存储</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_APIs/Third_party_APIs\">第三方 API</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Async_JS\">异步 JavaScript</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Async_JS/Introducing\">异步 JavaScript 简介</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Async_JS/Promises\">如何使用 Promise</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Async_JS/Implementing_a_promise-based_API\">如何实现基于 Promise 的 API</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Async_JS/Introducing_workers\">worker 简介</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Async_JS/Sequencing_animations\">序列动画</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Forms\">Web 表单</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Forms/Your_first_form\">创建我的第一个表单</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Forms/How_to_structure_a_web_form\">如何构建 HTML 表单</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Forms/Basic_native_form_controls\">原生表单部件</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Forms/HTML5_input_types\">HTML5 的输入(input)类型</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Forms/Other_form_controls\">其他表单控件</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Forms/Styling_web_forms\">样式化 Web 表单</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Forms/Advanced_form_styling\">表单样式化进阶</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Forms/UI_pseudo-classes\">UI 伪类</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Forms/Form_validation\">表单数据校验</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Forms/Sending_and_retrieving_form_data\">发送表单数据</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_tools\">理解客户端工具</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_tools/Overview\">客户端工具概述</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_tools/Package_management\">软件包管理基础</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_tools/Introducing_complete_toolchain\">介绍完整的工具链</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Client-side_tools/Deployment\">部署我们的应用</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side\">服务端网站</a></summary><ol><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/First_steps\">服务端基础</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/First_steps/Introduction\">服务端编程介绍</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/First_steps/Client-Server_overview\">客户端服务端交互概述</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/First_steps/Web_frameworks\">服务端 web 框架</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/First_steps/Website_security\">站点安全</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django\">Django web 框架(Python)</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Introduction\">Django 介绍</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/development_environment\">设置 Django 开发环境</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Tutorial_local_library_website\">Django Tutorial: The Local Library website</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/skeleton_website\">Django Tutorial Part 2: 创建网站的地基</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Models\">Django Tutorial Part 3: 使用模型</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Admin_site\">Django Tutorial Part 4: Django 管理员站点</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Home_page\">Django 教程 5:主页构建</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Generic_views\">Django 教程 6: 通用列表和详细信息视图</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Sessions\">Django 教程 7: 会话框架</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Authentication\">Django 教程 8:用户授权与许可</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Forms\">Django 教程 9: 使用表单</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Testing\">Django 教程 10: 测试 Django 网页应用</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/Deployment\">Django 教程 11:部署 Django 到生产环境</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/web_application_security\">Django Web 应用安全</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Django/django_assessment_blog\">评估:DIY Django 微博客</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs\">Express web 框架(Node.js)</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Introduction\">Express/Node 入门</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/development_environment\">设置 Node 开发环境</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Tutorial_local_library_website\">Express 教程:本地图书馆网站</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/skeleton_website\">Express 教程 2:创建站点框架</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/mongoose\">Express 教程 3:使用数据库 (Mongoose)</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/routes\">Express 教程 4:路由和控制器</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Displaying_data\">Express 教程 5: 呈现图书馆数据</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/forms\">Express 教程 6: 使用表单</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/deployment\">Express 教程 7:部署到生产环境</a></li></ol></details></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Performance\">Web 性能</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Performance/why_web_performance\">Web 性能的重要性</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Performance/What_is_web_performance\">什么是 web 性能?</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Performance/Perceived_performance\">感知性能</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Performance/Measuring_performance\">测量性能</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Performance/Multimedia\">多媒体:图片</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Performance/video\">多媒体:视频</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Performance/JavaScript\">JavaScript 性能优化</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Performance/HTML\">HTML 性能优化</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Performance/CSS\">CSS 性能优化</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Performance/business_case_for_performance\">web 性能的商业案例</a></li></ol></details></li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Testing\">测试</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Testing/Introduction\">跨浏览器测试介绍</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Testing/Testing_strategies\">测试的策略</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Testing/HTML_and_CSS\">处理常见的 HTML 和 CSS 问题</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Testing/Feature_detection\">实现特性检测</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Testing/Automated_testing\">自动化测试简介</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Extensions/Testing/Your_own_automation_environment\">搭建自己的自动化测试环境</a></li></ol></details></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Transform_animate\" class=\"only-in-en-us\">CSS 变换与动画化</a></li><li><a href=\"/en-US/docs/Learn_web_development/Extensions/Security_privacy\" class=\"only-in-en-us\">安全与隐私</a></li><li class=\"section\">更多资源</li><li class=\"toggle\"><details><summary><a href=\"/zh-CN/docs/Learn_web_development/Howto\">如何解决常见问题</a></summary><ol><li><a href=\"/zh-CN/docs/Learn_web_development/Howto/Solve_HTML_problems\">使用 HTML 解决常见问题</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Howto/Solve_CSS_problems\">解决常见的 CSS 问题</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Howto/Solve_JavaScript_problems\">解决 JavaSctript 代码的常见问题</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Howto/Web_mechanics\">Web 机制</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Howto/Tools_and_setup\">工具和安装</a></li><li><a href=\"/zh-CN/docs/Learn_web_development/Howto/Design_and_accessibility\">设计与无障碍</a></li></ol></details></li><li><a href=\"/en-US/docs/Learn_web_development/About\" class=\"only-in-en-us\">关于</a></li><li><a href=\"/en-US/docs/Learn_web_development/Educators\" class=\"only-in-en-us\">面向教育工作者的资源</a></li><li><a href=\"/en-US/docs/Learn_web_development/Changelog\" class=\"only-in-en-us\">更新日志</a></li></ol>","source":{"folder":"zh-cn/learn_web_development/extensions/client-side_apis/drawing_graphics","github_url":"https://github.com/mdn/translated-content/blob/main/files/zh-cn/learn_web_development/extensions/client-side_apis/drawing_graphics/index.md","last_commit_url":"https://github.com/mdn/translated-content/commit/216c73b669a9760f7a444ebfbcb04a68bb3669d9","filename":"index.md"},"summary":"浏览器包含一些非常强大的图形编程工具,从可缩放矢量图形(Scalable Vector Graphics,简称 SVG)语言到用于在 HTML <canvas> 元素上绘制图形的 API(参见 Canvas API 和 WebGL)。本文将介绍 canvas,并提供更多的学习资源。","title":"绘图","toc":[{"text":"Web 中的图形","id":"web_中的图形"},{"text":"主动学习:开始使用 &lt;canvas&gt;","id":"主动学习:开始使用_canvas"},{"text":"2D 画布基础","id":"2d_画布基础"},{"text":"循环和动画","id":"循环和动画"},{"text":"WebGL","id":"webgl"},{"text":"总结","id":"总结"},{"text":"参见","id":"参见"},{"text":"示例","id":"示例"}],"pageType":"learn-module-chapter"}}</script></body></html>

Pages: 1 2 3 4 5 6 7 8 9 10