<?xml version="1.0" encoding="utf-8"?>
<search>
  
  
  
  <entry>
    <title>Centos服务器上使用acme.sh脚本免费获取SSL泛域名证书并启用Https</title>
    <link href="/blog/posts/8f7d6897.html"/>
    <url>/blog/posts/8f7d6897.html</url>
    
    <content type="html"><![CDATA[<h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>每次申请<code>ssl</code>都觉得停麻烦的，还要各种拷贝和解析，每个二级域名还得每个申请。前几天进入自己的网站发现小绿标已经没了，取而代之的是不安全，登录购买域名的网站发现只能申请3个月免费的<code>SSL</code>证书。于是上网寻找其他的解决方案，发现<code>acme.sh</code>能解决我的问题。</p><img src="/blog/posts/8f7d6897/image-20240613104558834.png" class="" title="image-20240613104558834"><h4 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h4><p><code>acme.sh</code> 实现了 <code>acme</code>协议, 可以从 <code>LetsEncrypt</code> 生成免费的证书并自动续期<code>LetsEncrypt</code>免费<code>SSL</code>证书。</p><blockquote><p><code>Let&#39;s Encrypt</code>是一个于2015年三季度推出的数字证书认证机构，旨在以自动化流程消除手动创建和安装证书的复杂流程，并推广使万维网服务器的加密连接无所不在，为安全网站提供免费的SSL/TLS证书。<br>— 摘自 维基百科</p></blockquote><p>acme.sh 仓库地址：<a href="https://github.com/acmesh-official/acme.sh">acme.sh</a><br>acme.sh 中文说明：<a href="https://github.com/acmesh-official/acme.sh/wiki/说明">官方中文说明</a><br>各个 dnsapi 说明：<a href="https://github.com/acmesh-official/acme.sh/wiki/dnsapi">dnsapi</a></p><h4 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h4><p>安装 acme，后面my@example.com换成自己的邮箱</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">curl https://get.acme.sh | sh -s email=my@example.com<br></code></pre></td></tr></table></figure><p>如果上面官方下载地址失败 或者 太慢，可以选用国内的备用地址</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">curl https://gitcode.net/cert/cn-acme.sh/-/raw/master/install.sh?inline=<span class="hljs-literal">false</span> | sh -s email=my@example.com<br></code></pre></td></tr></table></figure><p>然后在 root 目录下<code>ls -a</code>就可以看到有一个<code>.acme.sh</code>的<a href="https://so.csdn.net/so/search?q=文件夹&amp;spm=1001.2101.3001.7020">文件夹</a>，进入后里面有个 account.conf 配置文件，里面有前面安装时填写的邮箱，不知道有什么用，估计到时候会给通知什么的吧</p><h6 id="设置别名-alias"><a href="#设置别名-alias" class="headerlink" title="设置别名 alias:"></a>设置别名 alias:</h6><figure class="highlight stata"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs stata">alias acme.<span class="hljs-keyword">sh</span>=~/.acme.<span class="hljs-keyword">sh</span>/acme.<span class="hljs-keyword">sh</span> <br></code></pre></td></tr></table></figure><p>进入.acme.sh 目录后使用下面命令开启 acme 自动更新</p><p>首先最好先切换一下证书类型，其他类型的证书可能访问出错</p><figure class="highlight stata"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs stata">acme.<span class="hljs-keyword">sh</span> --<span class="hljs-keyword">set</span>-default-<span class="hljs-keyword">ca</span> --server letsencrypt<br></code></pre></td></tr></table></figure><h4 id="验证"><a href="#验证" class="headerlink" title="验证"></a>验证</h4><p>通过官方说明文档可知，acme.sh 申请证书有两种方式，<a href="https://so.csdn.net/so/search?q=http&amp;spm=1001.2101.3001.7020">http</a> 和 dns 验证</p><p><strong>acme.sh</strong> 实现了 <strong>acme</strong> 协议支持的所有验证协议. 一般有两种方式验证: http 和 dns 验证.</p><h5 id="1-http-方式需要在你的网站根目录下放置一个文件-来验证你的域名所有权-完成验证-然后就可以生成证书了"><a href="#1-http-方式需要在你的网站根目录下放置一个文件-来验证你的域名所有权-完成验证-然后就可以生成证书了" class="headerlink" title="1. http 方式需要在你的网站根目录下放置一个文件, 来验证你的域名所有权,完成验证. 然后就可以生成证书了."></a>1. http 方式需要在你的网站根目录下放置一个文件, 来验证你的域名所有权,完成验证. 然后就可以生成证书了.</h5><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs awk">acme.sh --issue -d mydomain.com -d www.mydomain.com --webroot <span class="hljs-regexp">/home/</span>wwwroot<span class="hljs-regexp">/mydomain.com/</span><br></code></pre></td></tr></table></figure><p>只需要指定域名, 并指定域名所在的网站根目录. <strong>acme.sh</strong> 会全自动的生成验证文件, 并放到网站的根目录, 然后自动完成验证. 最后会聪明的删除验证文件. 整个过程没有任何副作用.</p><p>如果你用的 <strong>apache</strong>服务器, <strong>acme.sh</strong> 还可以智能的从 <strong>apache</strong>的配置中自动完成验证, 你不需要指定网站根目录:</p><figure class="highlight brainfuck"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs brainfuck"><span class="hljs-comment">acme</span><span class="hljs-string">.</span><span class="hljs-comment">sh</span> --<span class="hljs-comment">issue</span> <span class="hljs-literal">-</span><span class="hljs-comment">d</span> <span class="hljs-comment">mydomain</span><span class="hljs-string">.</span><span class="hljs-comment">com</span> --<span class="hljs-comment">apache</span><br></code></pre></td></tr></table></figure><p>如果你用的 <strong>nginx</strong>服务器, 或者反代, <strong>acme.sh</strong> 还可以智能的从 <strong>nginx</strong>的配置中自动完成验证, 你不需要指定网站根目录:</p><figure class="highlight brainfuck"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs brainfuck"><span class="hljs-comment">acme</span><span class="hljs-string">.</span><span class="hljs-comment">sh</span> --<span class="hljs-comment">issue</span> <span class="hljs-literal">-</span><span class="hljs-comment">d</span> <span class="hljs-comment">mydomain</span><span class="hljs-string">.</span><span class="hljs-comment">com</span> --<span class="hljs-comment">nginx</span><br></code></pre></td></tr></table></figure><p><strong>注意, 无论是 apache 还是 nginx 模式, acme.sh在完成验证之后, 会恢复到之前的状态, 都不会私自更改你本身的配置. 好处是你不用担心配置被搞坏, 也有一个缺点, 你需要自己配置 ssl 的配置, 否则只能成功生成证书, 你的网站还是无法访问https. 但是为了安全, 你还是自己手动改配置吧.</strong></p><p>如果你还没有运行任何 web 服务, <strong>80</strong> 端口是空闲的, 那么 <strong>acme.sh</strong> 还能假装自己是一个webserver, 临时听在<strong>80</strong> 端口, 完成验证:</p><figure class="highlight brainfuck"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs brainfuck"><span class="hljs-comment">acme</span><span class="hljs-string">.</span><span class="hljs-comment">sh</span> --<span class="hljs-comment">issue</span> <span class="hljs-literal">-</span><span class="hljs-comment">d</span> <span class="hljs-comment">mydomain</span><span class="hljs-string">.</span><span class="hljs-comment">com</span> --<span class="hljs-comment">standalone</span><br></code></pre></td></tr></table></figure><p>acme.sh脚本默认ca服务器是zerossl，经常出错，会导致获取证书的时候一直出现：Pending, The CA is processing your order, please just wait.</p><p>只需要把ca服务器改成letsencrypt 即可，虽然更改以后还是有概率出现pending，但基本2-3次即可成功</p><figure class="highlight stata"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs stata">acme.<span class="hljs-keyword">sh</span> --<span class="hljs-keyword">set</span>-default-<span class="hljs-keyword">ca</span> --server letsencrypt<br></code></pre></td></tr></table></figure><p>更高级的用法请参考: <a href="https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert">https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert</a></p><h5 id="2-手动-dns-方式-手动在域名上添加一条-txt-解析记录-验证域名所有权"><a href="#2-手动-dns-方式-手动在域名上添加一条-txt-解析记录-验证域名所有权" class="headerlink" title="2. 手动 dns 方式, 手动在域名上添加一条 txt 解析记录, 验证域名所有权."></a>2. 手动 dns 方式, 手动在域名上添加一条 txt 解析记录, 验证域名所有权.</h5><p>这种方式的好处是, 你不需要任何服务器, 不需要任何公网 ip, 只需要 dns 的解析记录即可完成验证. 坏处是，如果不同时配置 Automatic DNS API，使用这种方式 acme.sh 将无法自动更新证书，每次都需要手动再次重新解析验证域名所有权。</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs vim">acme.<span class="hljs-keyword">sh</span> --issue --dns -d mydomain.<span class="hljs-keyword">com</span> \<br> --yes-I-know-dns-manual-<span class="hljs-keyword">mode</span>-enough-<span class="hljs-keyword">go</span>-ahead-please<br></code></pre></td></tr></table></figure><p>然后, <strong>acme.sh</strong> 会生成相应的解析记录显示出来, 你只需要在你的域名管理面板中添加这条 txt 记录即可.</p><p>等待解析完成之后, 重新生成证书:</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs vim">acme.<span class="hljs-keyword">sh</span> --renew -d mydomain.<span class="hljs-keyword">com</span> \<br>  --yes-I-know-dns-manual-<span class="hljs-keyword">mode</span>-enough-<span class="hljs-keyword">go</span>-ahead-please<br></code></pre></td></tr></table></figure><p>注意第二次这里用的是 <code>--renew</code></p><p>dns 方式的真正强大之处在于可以使用域名解析商提供的 api 自动添加 txt 记录完成验证.</p><p><strong>acme.sh</strong> 目前支持 cloudflare, dnspod, cloudxns, godaddy 以及 ovh 等数十种解析商的自动集成.</p><p>以 dnspod 为例, 你需要先登录到 dnspod 账号, 生成你的 api id 和 api key, 都是免费的. 然后:</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs routeros"><span class="hljs-builtin-name">export</span> <span class="hljs-attribute">DP_Id</span>=<span class="hljs-string">&quot;1234&quot;</span><br><span class="hljs-builtin-name">export</span> <span class="hljs-attribute">DP_Key</span>=<span class="hljs-string">&quot;sADDsdasdgdsf&quot;</span><br>acme.sh --issue --dns dns_dp -d aa.com -d www.aa.com<br></code></pre></td></tr></table></figure><p>证书就会自动生成了. 这里给出的 api id 和 api key 会被自动记录下来, 将来你在使用 dnspod api 的时候, 就不需要再次指定了. 直接生成就好了:</p><figure class="highlight brainfuck"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs brainfuck"><span class="hljs-comment">acme</span><span class="hljs-string">.</span><span class="hljs-comment">sh</span> --<span class="hljs-comment">issue</span> <span class="hljs-literal">-</span><span class="hljs-comment">d</span> <span class="hljs-comment">mydomain2</span><span class="hljs-string">.</span><span class="hljs-comment">com</span> --<span class="hljs-comment">dns</span>  <span class="hljs-comment">dns_dp</span><br></code></pre></td></tr></table></figure><p>更详细的 api 用法: <a href="https://github.com/Neilpang/acme.sh/blob/master/dnsapi/README.md">https://github.com/Neilpang/acme.sh/blob/master/dnsapi/README.md</a></p><p>我的是腾讯云的域名，需要腾讯云key到环境变量。<a href="https://console.cloud.tencent.com/cam/capi">访问密钥 - 控制台 (tencent.com)</a></p><img src="/blog/posts/8f7d6897/image-20240613111343856.png" class="" title="image-20240613111343856"><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">export</span> Tencent_SecretId=<span class="hljs-string">&quot;&quot;</span><br><span class="hljs-built_in">export</span> Tencent_SecretKey=<span class="hljs-string">&quot;&quot;</span><br></code></pre></td></tr></table></figure><h4 id="申请-amp-安装"><a href="#申请-amp-安装" class="headerlink" title="申请&amp;安装"></a>申请&amp;安装</h4><p>前面证书生成以后, 接下来需要把证书 copy 到真正需要用它的地方.</p><p>注意, 默认生成的证书都放在安装目录下: <code>~/.acme.sh/</code>, 请不要直接使用此目录下的文件, 例如: 不要直接让 nginx/apache 的配置文件使用这下面的文件. 这里面的文件都是内部使用, 而且目录结构可能会变化.</p><p>正确的使用方法是使用 <code>--install-cert</code> 命令,并指定目标位置, 然后证书文件会被copy到相应的位置, 例如:</p><h5 id="Apache-example"><a href="#Apache-example" class="headerlink" title="Apache example:"></a>Apache example:</h5><figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs gradle">acme.sh --install-cert -d example.com \<br>--cert-<span class="hljs-keyword">file</span>      <span class="hljs-regexp">/path/</span>to<span class="hljs-regexp">/certfile/i</span>n<span class="hljs-regexp">/apache/</span>cert.pem  \<br>--key-<span class="hljs-keyword">file</span>       <span class="hljs-regexp">/path/</span>to<span class="hljs-regexp">/keyfile/i</span>n<span class="hljs-regexp">/apache/</span>key.pem  \<br>--fullchain-<span class="hljs-keyword">file</span> <span class="hljs-regexp">/path/</span>to<span class="hljs-regexp">/fullchain/</span>certfile<span class="hljs-regexp">/apache/</span>fullchain.pem \<br>--reloadcmd     <span class="hljs-string">&quot;service apache2 force-reload&quot;</span><br></code></pre></td></tr></table></figure><h5 id="Nginx-example"><a href="#Nginx-example" class="headerlink" title="Nginx example:"></a>Nginx example:</h5><figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs gradle">acme.sh --install-cert -d example.com \<br>--key-<span class="hljs-keyword">file</span>       <span class="hljs-regexp">/path/</span>to<span class="hljs-regexp">/keyfile/i</span>n<span class="hljs-regexp">/nginx/</span>key.pem  \<br>--fullchain-<span class="hljs-keyword">file</span> <span class="hljs-regexp">/path/</span>to<span class="hljs-regexp">/fullchain/</span>nginx/cert.pem \<br>--reloadcmd     <span class="hljs-string">&quot;service nginx force-reload&quot;</span><br></code></pre></td></tr></table></figure><p>(一个小提醒, 这里用的是 <code>service nginx force-reload</code>, 不是 <code>service nginx reload</code>, 据测试, <code>reload</code> 并不会重新加载证书, 所以用的 <code>force-reload</code>)</p><p>Nginx 的配置 <code>ssl_certificate</code> 使用 <code>/etc/nginx/ssl/fullchain.cer</code> ，而非 <code>/etc/nginx/ssl/&lt;domain&gt;.cer</code> ，否则 <a href="https://www.ssllabs.com/ssltest/">SSL Labs</a> 的测试会报 <code>Chain issues Incomplete</code> 错误。</p><p><code>--install-cert</code>命令可以携带很多参数, 来指定目标文件. 并且可以指定 reloadcmd, 当证书更新以后, reloadcmd会被自动调用,让服务器生效.</p><p>详细参数请参考: <a href="https://github.com/Neilpang/acme.sh#3-install-the-issued-cert-to-apachenginx-etc">https://github.com/Neilpang/acme.sh#3-install-the-issued-cert-to-apachenginx-etc</a></p><p>值得注意的是, 这里指定的所有参数都会被自动记录下来, 并在将来证书自动更新以后, 被再次自动调用.</p><p>我选择<code>DNS</code>方式，在我<code>centos</code>服务器执行以下命令</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">acme.sh --install-cert --issue --dns dns_tencent -d gishai.top -d *.gishai.top --key-file   /path/to/certfile/cert/key.pem  --fullchain-file /path/to/certfile/cert/cert.pem --reloadcmd     <span class="hljs-string">&quot;service nginx force-reload&quot;</span><br></code></pre></td></tr></table></figure><p>nginx配置中修改路径</p><img src="/blog/posts/8f7d6897/image-20240613114837776.png" class="" title="image-20240613114837776"><h4 id="查看"><a href="#查看" class="headerlink" title="查看"></a>查看</h4><p>看看网站重新有了小绿标，证书看看还有多久过期。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sh">openssl x509 -<span class="hljs-keyword">in</span> /path/to/certfile/cert/cert.pem -noout -dates<br></code></pre></td></tr></table></figure><img src="/blog/posts/8f7d6897/image-20240613112451383.png" class="" title="image-20240613112451383"><img src="/blog/posts/8f7d6897/image-20240613115714065.png" class="" title="image-20240613115714065"><p>二级域名的<code>ssl</code>也是可以的。</p><p>看看有木有自动任务，因为证书3个月就过期了。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sh">crontab  -l<br></code></pre></td></tr></table></figure><img src="/blog/posts/8f7d6897/image-20240613112739271.png" class="" title="image-20240613112739271"><p>目前一切正常，两个月后再看看</p>]]></content>
    
    
    
  </entry>
  
  
  
  <entry>
    <title>arcgis server站点服务的的迁移与恢复</title>
    <link href="/blog/posts/3cb1af17.html"/>
    <url>/blog/posts/3cb1af17.html</url>
    
    <content type="html"><![CDATA[<div class="note note-success">            <p>本文转载，版权归原作者所有。</p> <p>本文作者：GIS学习小屋<br>原文地址：<a href="https://mp.weixin.qq.com/s/eEExwtgIoot2cdu9jKZHkw">https://mp.weixin.qq.com/s/eEExwtgIoot2cdu9jKZHkw</a></p>           </div><p>​        通常我们会遇到因为服务器问题或者ArcGIS Server站点异常需要对服务整体迁移的情况，如果重新发布所有服务耗时耗力，在这种情况下我们可以采用迁移的方式来恢复站点。以下方案适用于在同一台机器迁移或者从A机器迁移至B机器。</p><p>方式一：通过手动拷贝文件夹替换的方式迁移</p><p>情景1：保持config-store和directories物理路径相同、迁移前后的arcgis server account用户名和密码保持一致、数据源的存储位置不变。这种是最为简单的迁移模式，具体步骤如下：</p><p>1、先手动备份下原始环境里arcgisserver整个文件夹</p><p>2、在新的机器上安装server ，或者在原有机器上将 server卸载掉然后安装 arcgis server，并且创建空站点，确保config-store和directories物理路径和原始站点相同</p><p>3、停止新环境server的系统服务，将步骤1备份文件夹里的config-store下services、data和directories 目录拷贝到新创建的site对应的目录下，覆盖对应目录。</p><p>4、拷贝过来的arcgisserver目录，由于拷贝丢失了相关的权限信息。在文件夹属性的安全中，赋予server 账户的完全控制权限。</p><p>5、 启动server系统服务。</p><p>情景2：config-store和directories物理路径不一致，arcgis server account用户名和密码一致、数据源的存储位置不变的情况。</p><p>1、先按照上述情景1的步骤进行操作</p><p>2、操作完成后，先停止server系统服务，需要找到服务的配置文件，修改msd的路径指向新的路径。</p><p>(1) 路径1：在arcgisinput文件夹对应服务目录下的manifest.json和manifest.xml文件（如arcgisinput\服务名称.MapServer\extracted\）</p><p>(2) 路径2：在config-store的services目录中找到对应的服务目录，修改服务名对应的json文件中的相对应路径(config-store\services\服务名.MapServer\服务名.MapServer.json），高版本的server不需要修改此文件。</p><p>3、启动server系统服务。</p><p>情景3：上面的情景中都是数据源不变的情况，但是在实际的情况下，有可能出现数据源发生变化的情况，如：</p><p>1、如果发布服务的时候数据存储在filegdb中，现数据存储转存到共享存储中；</p><p>(1) 先按照上述情景1的步骤进行操作</p><p>(2) 停止server服务，将新的共享文件夹注册到server注册，然后需要修复server端的mxd文档中数据源的路径指向新路径，再通过新的mxd生成新的msd文件，覆盖替换v101文件夹下的mxd及msd文件。具体路径为</p><p><code>arcgisserver\directories\arcgissystem\arcgisinput\xxx.MapServer\extracted\v101</code></p><p>生成msd方法有：</p><p>a、<a href="https://blog.csdn.net/smss007/article/details/82993749">https://blog.csdn.net/smss007/article/details/82993749</a></p><p>b、arcpy脚本生成</p><p>(3) 启动server系统服务</p><p>2、如果更改了server中的服务的数据源的连接字符串(数据库的用户名，密码或者ip发生了变化)，导致通过注册到server的旧的sde连接字符串没有办法访问新的数据库。</p><p>(1) server 10.4.1之后的版本：</p><p>  a、先通过arcmap创建新的sde文件，确保Server也可以通过连接文件中的ip、端口和实例等信息访问到SDE库。</p><p>  b、其次，在manager中点击站点-&gt;数据存储，找到之前注册的sde连接的那个条目，点击右侧的编辑，导入步骤1中创建的sde文件。</p><p>(2) server 10.4.1之前的版本：</p><p>  a、先通过arcmap创建新的sde文件</p><p>  b、通过上述替换msd的方式。</p><p>方式二、使用server安装目录自带的备份还原工具来恢复站点。</p><p>备份工具详见：</p><p><a href="https://enterprise.arcgis.com/zh-cn/server/10.8/administer/windows/backup-utility.htm">https://enterprise.arcgis.com/zh-cn/server/10.8/administer/windows/backup-utility.htm</a></p><p>还原工具详见：</p><p><a href="https://enterprise.arcgis.com/zh-cn/server/10.8/administer/windows/restore-utility.htm">https://enterprise.arcgis.com/zh-cn/server/10.8/administer/windows/restore-utility.htm</a></p><p>详细说明见：</p><p><a href="https://enterprise.arcgis.com/zh-cn/server/10.8/administer/windows/back-up-and-restore-your-arcgis-server-site-configuration.htm">https://enterprise.arcgis.com/zh-cn/server/10.8/administer/windows/back-up-and-restore-your-arcgis-server-site-configuration.htm</a></p><p>具体步骤：</p><p>1、在新环境中安装ArcGIS Server，或者在原有环境中卸载server然后安装arcgis server。</p><p>2、在原环境中运行备份工具，备份文件存到指定目录，如以下示例放置于sbackup文件夹创建备份，生成一个.agssite文件：</p><figure class="highlight taggerscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs taggerscript">&lt;Python installation location&gt;<span class="hljs-symbol">\p</span>ython.exe &quot;D:<span class="hljs-symbol">\P</span>rogram Files<span class="hljs-symbol">\A</span>rcGIS<span class="hljs-symbol">\S</span>erver<span class="hljs-symbol">\t</span>ools<span class="hljs-symbol">\a</span>dmin<span class="hljs-symbol">\b</span>ackup.py&quot; -u siteadmin -p admin123 -s https://myserver:6443 -f &quot;d:<span class="hljs-symbol">\s</span>backup&quot;<br></code></pre></td></tr></table></figure><p>3、安装server后，创建空站点，配置目录路径和原始站点保持一致。</p><p>4、将原环境上的.agssite文件拷贝至新机器，在新机器上运行还原工具：</p><figure class="highlight taggerscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs taggerscript">&lt;Python installation location&gt;<span class="hljs-symbol">\p</span>ython.exe &quot;D:<span class="hljs-symbol">\P</span>rogram Files<span class="hljs-symbol">\A</span>rcGIS<span class="hljs-symbol">\S</span>erver<span class="hljs-symbol">\t</span>ools<span class="hljs-symbol">\a</span>dmin<span class="hljs-symbol">\r</span>estore.py&quot; -u siteadmin -p admin123 -s https://gisserver.domain.com:6443 -f d:<span class="hljs-symbol">\s</span>backup<span class="hljs-symbol">\d</span>atatest.agssite -r d:<span class="hljs-symbol">\s</span>backup<br></code></pre></td></tr></table></figure><p>参数说明：</p><p>-u : 主站点管理员帐户</p><p>-p：站点用户的密码</p><p>-s:  站点的URL ：</p><p><a href="https://gisserver.domain.com:6443">https://gisserver.domain.com:6443</a>  或 <a href="http://gisserver.domain.com:6080">http://gisserver.domain.com:6080</a></p><p>-f : 需要存储的 .agssite 备份文件的绝对路径。ArcGIS Server 帐户必须具有从此位置的读取权限。</p><p>-r: 此实用程序生成的报告所在文件夹的路径。ArcGIS Server 帐户必须具有从此位置的读取权限。</p><p>5、将原机器的缓存切片文件夹手动拷贝至新站点对应的位置：</p><p>（arcgisserver\directories\ 下的整个 arcgiscache 目录）</p>]]></content>
    
    
    
  </entry>
  
  
  
  <entry>
    <title>mockjs导致cesium地图加载出错</title>
    <link href="/blog/posts/ab207de9.html"/>
    <url>/blog/posts/ab207de9.html</url>
    
    <content type="html"><![CDATA[<p> 在vue中使用超图的<code>@supermap/iclient3d-webgl</code>出现地图加载出错的问题</p><img src="/blog/posts/ab207de9/image-20240509094530341.png" class="" title="image-20240509094530341"><p>面向搜索引擎一番，没有发现类似问题，然后换原生<code>Cesium</code>加载试试，得到的是一个没有底图的球</p><img src="/blog/posts/ab207de9/image-20240509100843675.png" class="" title="image-20240509100843675"><p>报错信息如下<img src="/blog/posts/ab207de9/image-20240509100832735.png" class="" title="image-20240509100832735"></p><p>于是仔细研究报错信息，发现它说的是提供的数据格式不对，开<code>F12</code>查看请求数据，好像也没啥异常</p><img src="/blog/posts/ab207de9/image-20240509100724892.png" class="" title="image-20240509100724892"><p>偶然见看见掘金一篇文章<a href="https://juejin.cn/post/7143579664424894472">mockjs 导致cesium地图无法加载 - 掘金 (juejin.cn)</a></p><p>按照文章的方法修改mock.js的源码。 代码路径：项目//node_modules/mockjs/dist/mock.js 大约在8360行：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// 原生 XHR</span><br>                <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.match) &#123;<br>                    <span class="hljs-built_in">this</span>.custom.xhr.send(data)<br>                    <span class="hljs-keyword">return</span><br>                &#125;<br><br></code></pre></td></tr></table></figure><p>修改为</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// 原生 XHR</span><br><br>                <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.match) &#123;<br>                    <span class="hljs-built_in">this</span>.custom.xhr.responseType = <span class="hljs-built_in">this</span>.responseType<br>                    <span class="hljs-built_in">this</span>.custom.xhr.send(data)<br>                    <span class="hljs-keyword">return</span><br>                &#125;<br><br></code></pre></td></tr></table></figure><p>地球底图成功显示出来，但是这个方法不好，其他同事还得修改源码，重写mock的send方法,其实最主要的是修改<code>responseType</code>,因为这mock源代码中,<code>responseType</code>是直接为空的</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs js">Mock.XHR.prototype.send = (<span class="hljs-function">() =&gt;</span> &#123;<br>  <span class="hljs-keyword">const</span> _send = Mock.XHR.prototype.send<br><br>  <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;<br>    <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.match) &#123;<br>      <span class="hljs-built_in">this</span>.custom.xhr.responseType = <span class="hljs-built_in">this</span>.responseType || <span class="hljs-string">&#x27;&#x27;</span><br><br>      <span class="hljs-built_in">this</span>.custom.xhr.timeout = <span class="hljs-built_in">this</span>.timeout || <span class="hljs-number">0</span><br><br>      <span class="hljs-built_in">this</span>.custom.xhr.withCredentials = <span class="hljs-built_in">this</span>.withCredentials || <span class="hljs-literal">false</span><br><br>      <span class="hljs-built_in">this</span>.custom.xhr.onabort = <span class="hljs-built_in">this</span>.onabort || <span class="hljs-literal">null</span><br><br>      <span class="hljs-built_in">this</span>.custom.xhr.onerror = <span class="hljs-built_in">this</span>.onerror || <span class="hljs-literal">null</span><br><br>      <span class="hljs-built_in">this</span>.custom.xhr.onload = <span class="hljs-built_in">this</span>.onload || <span class="hljs-literal">null</span><br><br>      <span class="hljs-built_in">this</span>.custom.xhr.onloadend = <span class="hljs-built_in">this</span>.onloadend || <span class="hljs-literal">null</span><br><br>      <span class="hljs-built_in">this</span>.custom.xhr.onloadstart = <span class="hljs-built_in">this</span>.onloadstart || <span class="hljs-literal">null</span><br><br>      <span class="hljs-built_in">this</span>.custom.xhr.onprogress = <span class="hljs-built_in">this</span>.onprogress || <span class="hljs-literal">null</span><br><br>      <span class="hljs-built_in">this</span>.custom.xhr.onreadystatechange = <span class="hljs-built_in">this</span>.onreadystatechange || <span class="hljs-literal">null</span><br><br>      <span class="hljs-built_in">this</span>.custom.xhr.ontimeout = <span class="hljs-built_in">this</span>.ontimeout || <span class="hljs-literal">null</span><br>    &#125;<br><br>    <span class="hljs-keyword">return</span> _send.apply(<span class="hljs-built_in">this</span>, <span class="hljs-built_in">arguments</span>)<br>  &#125;<br>&#125;)()<br><br></code></pre></td></tr></table></figure><p>最后小破球就成功显示出来了</p><img src="/blog/posts/ab207de9/image-20240509101656686.png" class="" title="image-20240509101656686"><p>>参考文章：</p><p>></p><p>>- <a href="https://blog.csdn.net/qq_29559239/article/details/129417777">vue2+cesium加载离线地图报错_failed to execute ‘createimagebitmap’ on ‘window’:-CSDN博客</a></p><p>>- <a href="https://juejin.cn/post/7143579664424894472">mockjs 导致cesium地图无法加载 - 掘金 (juejin.cn)</a></p><p>>- <a href="https://blog.csdn.net/ok2547881370/article/details/110820740">记录一次使用mockjs出现诡异问题_at gltfloader.parse-CSDN博客</a></p>]]></content>
    
    
    
    <tags>
      
      <tag>cesium</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>centos使用npm安装不上sqlite3的记录</title>
    <link href="/blog/posts/f16721cc.html"/>
    <url>/blog/posts/f16721cc.html</url>
    
    <content type="html"><![CDATA[<p>在centos跑一个node服务时，npm或者yarn安装sqlite3无法成功的问题。一度怀疑是我的node版本太高了，想卸载安装低版本的node。谷歌问题搜索到以下答案。</p><img src="/blog/posts/f16721cc/image-20231108144002203-9425604.png" class="" title="image-20231108144002203"><img src="/blog/posts/f16721cc/image-20231108144214941-9425739.png" class="" title="image-20231108144214941"><p>综上所述就是python版本指定问题。</p><p>然后我就百度npm怎么设置指定python</p><img src="/blog/posts/f16721cc/image-20231108144333959-9425817.png" class="" title="image-20231108144333959"><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs shell">npm config set python /usr/bin/python3<br>npm config set python-version 3.8.5<br>npm config set python-path /usr/local/bin/python<br></code></pre></td></tr></table></figure><p>设置完再npm i就一次性成功了。</p>]]></content>
    
    
    
  </entry>
  
  
  
  <entry>
    <title>微软New Bing申请步骤</title>
    <link href="/blog/posts/318bf2e9.html"/>
    <url>/blog/posts/318bf2e9.html</url>
    
    <content type="html"><![CDATA[<img src="/blog/posts/318bf2e9/microsoft-bing-chatgpt-6431035.jpeg" class="" title="microsoft-bing-chatgpt"><p>随着chatGPT火爆互联网，微软宣布自己的搜索引擎必应（Bing）将接入chatGPT，微软用户只需加入候补名单就有机会抢先体验该功能，如果直接在浏览器地址栏输入<a href="https://bing.com/new">https://bing.com/new</a> 回车的话会自动跳转到 <a href="https://cn.bing.com/?form=REDIRERR">https://cn.bing.com/?form=REDIRERR</a>  这个地址。这是没办法去申请候补名单的。那么应该怎么申请资格呢？接下来就一起来了解一下吧。</p><img src="/blog/posts/318bf2e9/20230213170458_87049.jpg" class="" title="img"><p><strong>微软New Bing申请教程</strong></p><p>1、使用Win10/Win11自带浏览器Edge打开安装“<strong>ModHeader</strong>”扩展程序</p><p><a href="https://microsoftedge.microsoft.com/addons/detail/modheader-modify-http-h/opgbiafapkbbnbnjcdomjaghbckfkglc">&gt;&gt;扩展程序安装入口&lt;&lt;</a></p><img src="/blog/posts/318bf2e9/20230213170458_54195.jpg" class="" title="img"><p>2、设置扩展 填写字段一: <strong>X-Forwarded-For</strong> 填写字段二: <strong>4.2.2.2</strong></p><img src="/blog/posts/318bf2e9/20230213170459_12963.jpg" class="" title="img"><p>3、完成上述操作后登录New Bing官网登录微软账号点击“<strong>加入等待列表</strong>”即可，想要更快访问New Bing可以选择下载官方APP</p><p><a href="https://www.bing.com/new">&gt;&gt;官网入口&lt;&lt;</a></p><img src="/blog/posts/318bf2e9/20230213170459_68515.jpg" class="" title="img"><p>4、等等等等等</p>]]></content>
    
    
    
    <tags>
      
      <tag>技巧</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>rollup编译的小型库</title>
    <link href="/blog/posts/7c78b87b.html"/>
    <url>/blog/posts/7c78b87b.html</url>
    
    <content type="html"><![CDATA[<h2 id="📖阅读本文，你将"><a href="#📖阅读本文，你将" class="headerlink" title="📖阅读本文，你将:"></a><strong>📖阅读本文，你将:</strong></h2><ol><li>手把手带你搭建一个极简 <code>npm</code> 包（ <code>rollup</code> + <code>esbuild</code> + <code>ts</code> ）；</li><li>从工程结构全方位思考 <code>npm</code> 包的开发原则。</li><li>学会 <code>rollup</code>、<code>esbuild</code> 等新兴构建工具的使用；</li></ol><h2 id="一、前置阅读（推荐）"><a href="#一、前置阅读（推荐）" class="headerlink" title="一、前置阅读（推荐）"></a><strong>一、前置阅读（推荐）</strong></h2><blockquote><p>本文章不再赘述 <code>rollup</code> 和 官方 <code>rollup</code> 插件的使用，因此有需要的话建议先阅读前置文章：</p></blockquote><ol><li>说不清rollup能输出哪6种格式😥差点被鄙视</li><li>一文入门rollup🪀！13组demo带你轻松驾驭</li></ol><p>如果你对 <code>rollup</code> 和其官方插件有了一定了解，可跳过本节推荐的相关阅读；</p><h2 id="二、目标"><a href="#二、目标" class="headerlink" title="二、目标"></a><strong>二、目标</strong></h2><p>先制定一个小目标：</p><blockquote><p>从零构建一个通过 <code>rollup</code> 构建的 <code>npm</code> 包；它可发布、可调试、支持 <code>TypeScript</code>、能够通过 <code>cdn</code> 引入页面、也可以通过 <code>npm</code> 在项目里安装引入。</p></blockquote><p>这个目标有哪些关键词？让我们拆解一下：</p><ol><li>是一个 <strong>可发布</strong> 的 <code>npm</code> 包；</li><li>通过 <code>rollup</code> 构建；</li><li>支持 <strong>调试</strong> ；</li><li>支持 <strong>TypeScript</strong> ;</li><li>能通过 <strong><code>cdn</code></strong> 方式引入</li><li>也支持通过 <code>npm</code> 安装；</li></ol><p>看似简单，其实内中蕴含着许多前端相关的知识点，让我们一一梳理；</p><h2 id="三、典型-npm-包的结构"><a href="#三、典型-npm-包的结构" class="headerlink" title="三、典型 npm 包的结构"></a><strong>三、典型 <code>npm</code> 包的结构</strong></h2><p>我们发布一个 <code>npm</code> 包时，我们在发布什么？</p><ol><li>一个用来声明 <code>npm</code> 属性的 <code>package.json</code> 文件；</li><li>若干可执行、可引入的文件；</li></ol><p>仅此而已。</p><p>只有 <code>package.json</code> 是特殊的，除此之外的一切都可以被归为 “若干文件” 之列；</p><p>找一个最典型、最简单的 <code>npm</code> 包来印证我们的想法，你可以随便找个项目，执行以下命令：</p><figure class="highlight coffeescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs coffeescript"><span class="hljs-built_in">npm</span> i the-answer<br><span class="hljs-comment"># 传说，此库以 “功能丰富”、“毫无破绽” 闻名于世</span><br></code></pre></td></tr></table></figure><p>然后找到 <code>node_modules/the-answer</code> 文件夹，看看其项目目录:</p><p>是以，要理解 <code>npm</code> 包，必须先从 <code>package.json</code> 入手，而一个典型的 <code>package.json</code> 至少应该具备以下属性：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs json">&#123;<br>  <span class="hljs-attr">&quot;name&quot;</span>: <span class="hljs-string">&quot;the-answer&quot;</span>,<br>  <span class="hljs-attr">&quot;version&quot;</span>: <span class="hljs-string">&quot;1.0.0&quot;</span>,<br>  <span class="hljs-attr">&quot;description&quot;</span>: <span class="hljs-string">&quot;description&quot;</span>,<br>  <span class="hljs-attr">&quot;main&quot;</span>: <span class="hljs-string">&quot;dist/the-answer.js&quot;</span>,<br>  <span class="hljs-attr">&quot;files&quot;</span>: [<br>    <span class="hljs-string">&quot;dist&quot;</span><br>  ]<br>&#125;<br></code></pre></td></tr></table></figure><p>以上内容分别表示：</p><ul><li><code>name</code>: 包名</li><li><code>version</code>: 版本</li><li><code>description</code>: 包描述</li><li><code>main</code>：入口</li><li><code>files</code>：包发布时，需要将哪些文件打包上传</li></ul><p>以上5个属性，足以支撑一个 <code>npm</code> 包的发布，但后续我们可能还会用到更多，此时，请先记住它们；</p><p>那么其他文件呢？</p><p>“协议” 和 “文档” 可以手动创建，但是 “构建物” 通常难逃 “构建” 这一过程。</p><p><code>rollup</code> 是个不错的选择；</p><h2 id="四、-rollup-的最简用法"><a href="#四、-rollup-的最简用法" class="headerlink" title="四、 rollup 的最简用法"></a><strong>四、 <code>rollup</code> 的最简用法</strong></h2><h2 id="4-1-安装-rollup"><a href="#4-1-安装-rollup" class="headerlink" title="4.1 安装 rollup"></a><strong>4.1 安装 <code>rollup</code></strong></h2><p>执行脚本：</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">npm i -D <span class="hljs-keyword">rollup</span><br># <span class="hljs-keyword">or</span><br>yarn <span class="hljs-keyword">add</span> -D <span class="hljs-keyword">rollup</span><br></code></pre></td></tr></table></figure><h2 id="4-2-创建-rollup-config-js"><a href="#4-2-创建-rollup-config-js" class="headerlink" title="4.2 创建 rollup.config.js"></a><strong>4.2 创建 <code>rollup.config.js</code></strong></h2><blockquote><p><code>rollup.config.js</code> 是 <code>rollup</code> 默认的配置文件</p></blockquote><p>在这个配置文件里，最简单的配置莫过于：</p><ul><li>配置入口文件</li><li>配置输出路径&amp;格式</li></ul><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs lua">export default &#123;<br>  <span class="hljs-built_in">input</span>: <span class="hljs-built_in">path</span>.resolve(__dirname, <span class="hljs-string">&#x27;./src/index.ts&#x27;</span>),<br>  <span class="hljs-built_in">output</span>: [<br>    &#123;<br>      dir: <span class="hljs-built_in">path</span>.resolve(__dirname, <span class="hljs-string">&#x27;dist/esm&#x27;</span>),<br>      <span class="hljs-built_in">format</span>: <span class="hljs-string">&#x27;esm&#x27;</span>, // 通过esm格式输出<br>    &#125;<br>  ]<br>&#125;<br></code></pre></td></tr></table></figure><h2 id="4-3-编写构建脚本"><a href="#4-3-编写构建脚本" class="headerlink" title="4.3 编写构建脚本"></a><strong>4.3 编写构建脚本</strong></h2><p>在 <code>package.json</code> 文件中添加如下代码：</p><figure class="highlight clojure"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs clojure">&#123;<br>  ...,<br>  <span class="hljs-string">&quot;scripts&quot;</span>: &#123;<br>    <span class="hljs-string">&quot;build&quot;</span>: <span class="hljs-string">&quot;rollup -c&quot;</span><br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>编写完上面脚本后，只需要执行：<code>npm run build</code>，就能成功完成一个最简单的 <code>rollup</code> 构建了。</p><p>但这还远远不够。</p><h2 id="五、梳理：构建过程应该输出什么？"><a href="#五、梳理：构建过程应该输出什么？" class="headerlink" title="五、梳理：构建过程应该输出什么？"></a><strong>五、梳理：构建过程应该输出什么？</strong></h2><blockquote><p>构建是为了有输出，输出是为了给客户使用。</p></blockquote><p>因此，当我们构建时，应该率先考虑客户会在什么场景下使用我们的库。</p><p>（哪怕没有人真的用，但我们得假装自己的库会非常受欢迎，不是吗？🤣）</p><p>最常见的三种情况：</p><ul><li>在构建工具中引入（<code>esm</code>）</li><li>在页面中直接引入（<code>umd</code>）</li><li>在<code>nodejs</code> 环境中使用 (<code>cjs</code>)</li></ul><p>而本文的主要目标不涉及 <code>nodejs</code> 环境，因此暂时不考虑 <code>cjs</code> 输出，所以我们应该输出至少两种格式：</p><ol><li><code>esm</code></li><li><code>umd</code></li></ol><p>关于以上格式的区别，以及怎么输出，前置阅读推荐《说不清rollup能输出哪6种格式😥差点被鄙视》里有详细介绍；</p><p>通过参考目前开源社区的实现方式，我们还可以发现，通常一个开源库的 <code>esm</code> 产出物，并不是和 <code>rollup</code> 默认行为那样“把所有文件打包成一个 <code>index.esm.js</code>”，而是：</p><p><strong>只编译，不打包</strong>；（这样就能完美支持按需引入了）</p><img src="/blog/posts/7c78b87b/640" class="" title="图片"><p>是的，文件结构依然和源码结构保持一致，只是将 <code>TypeScript</code> 编译为 <code>javascript</code>；</p><p>记住这个目标，它很重要；</p><h2 id="六、编译-TS-哪家强？"><a href="#六、编译-TS-哪家强？" class="headerlink" title="六、编译 TS 哪家强？"></a><strong>六、编译 <code>TS</code> 哪家强？</strong></h2><blockquote><p>虽然有提案让 <code>ts</code> 成为下一代的浏览器语言，但这只是还未到来的未来。</p></blockquote><p>因此，如果我们的库是 <code>TypeScript</code> 编写的，那么我们在发布之前，一定需要将其编译为 <code>javascript</code> 类型。</p><p>那么，应该怎么编译呢？</p><p>社区里目前的主流方案有三类：</p><p>a. <code>TypeScript</code> <a href="https://www.npmjs.com/package/typescript">https://www.npmjs.com/package/typescript</a></p><p>b. <code>Esbuild</code> <a href="https://esbuild.github.io/">https://esbuild.github.io/</a></p><p>c. <code>Babel</code> <a href="https://babeljs.io/">https://babeljs.io/</a></p><p>通过这篇文章：<a href="https://juejin.cn/post/7083298517753528334">https://juejin.cn/post/7083298517753528334</a> 的对比分析，可以得出结论：</p><p><code>【c】</code> 比 <code>【a】</code> 支持的语言更丰富、编译速度更快；但是 <code>.d.ts</code> 还是推荐通过 <code>【a】</code> 中的 <code>tsc</code> 编译生成；</p><p>那么 <code>【b】</code> 选项 <code>Esbuild</code> 呢？</p><p>答案是：<strong>速度更快</strong> ！</p><p>所以，我做出了如下选择：</p><ul><li>使用 <code>esbuild</code> 进行 <code>ts =&gt; js</code> 的编译</li><li>使用 <code>TypeScript</code> 进行 <code>.d.ts</code> 文件的编译；</li></ul><h2 id="七、编译成-esm-格式"><a href="#七、编译成-esm-格式" class="headerlink" title="七、编译成 esm 格式"></a><strong>七、编译成 <code>esm</code> 格式</strong></h2><p>整个构建过程梳理清楚后，我们可以在项目里加入如下依赖：</p><figure class="highlight elm"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs elm"><span class="hljs-title">npm</span> i typescript esbuild rollup-plugin-esbuild <span class="hljs-comment">--save-dev</span><br></code></pre></td></tr></table></figure><p>除了以上这些之外，还有一些常规 <code>rollup</code> 插件不在此赘述；</p><p>创建文件：<code>rollup.esm.config.js</code></p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">export <span class="hljs-keyword">default</span> &#123;<br>  <span class="hljs-keyword">input</span>: <span class="hljs-type">path</span>.resolve(__dirname, <span class="hljs-string">&#x27;./src/index.ts&#x27;</span>),<br>  output: [<br>    &#123;<br>      dir: <span class="hljs-type">path</span>.resolve(__dirname, <span class="hljs-string">&#x27;dist/esm&#x27;</span>),<br>      <span class="hljs-keyword">format</span>: <span class="hljs-string">&#x27;esm&#x27;</span>,<br>    &#125;<br>  ],<br>  // 关键属性，只有将其设置为 `<span class="hljs-keyword">true</span>` 才能保证只编译、不打包<br>  preserveModules: <span class="hljs-keyword">true</span>,<br>  plugins: [<br>    esbuild(&#123;<br>      target: <span class="hljs-string">&#x27;es2018&#x27;</span><br>    &#125;),<br>    nodeResolve(),<br>    <span class="hljs-type">json</span>(),<br>  ],<br>  // 在 `esm` 构件中，`<span class="hljs-keyword">external</span>` 非常重要，项目的 `dependencies` 里的内容理应都在此列<br>  <span class="hljs-keyword">external</span>: [<span class="hljs-string">&#x27;gsap&#x27;</span>]<br>&#125;;<br></code></pre></td></tr></table></figure><p>编写如下脚本：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs json">&#123;<br>  <span class="hljs-attr">&quot;scripts&quot;</span>: &#123;<br>    <span class="hljs-attr">&quot;build:esm&quot;</span>: <span class="hljs-string">&quot;rollup -c rollup.esm.config.js&quot;</span>,<br>    <span class="hljs-attr">&quot;postbuild:esm&quot;</span>: <span class="hljs-string">&quot;tsc  --emitDeclarationOnly --declaration --project ts.config.json --outDir dist/esm&quot;</span>,<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>解释一下，<code>postbuild:esm</code> 是 <code>build:esm</code> 脚本的后置钩子，当 <code>build:esm</code> 执行完成后会自动执行 <code>postbuild:esm</code>；</p><p>执行脚本：</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs routeros">npm <span class="hljs-builtin-name">run</span> build:esm<br></code></pre></td></tr></table></figure><p>可以编译出如下效果的代码：</p><img src="/blog/posts/7c78b87b/640" class="" title="图片"><p>基本满足我们对 <code>esm</code> 格式编译产出的要求。</p><h2 id="八、编译-umd-格式"><a href="#八、编译-umd-格式" class="headerlink" title="八、编译 umd 格式"></a><strong>八、编译 <code>umd</code> 格式</strong></h2><p>和 <code>esm</code> 格式的编译要求不同，<code>umd</code> 格式更多的被用在 <code>cdn</code> 加载。</p><p>因此：</p><ul><li>构建物不仅需要编译，还需要打包</li><li>无需 <code>.d.ts</code> 文件</li></ul><p>创建 <code>rollup.umd.config.js</code>:</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">export <span class="hljs-keyword">default</span> &#123;<br>  <span class="hljs-keyword">input</span>: <span class="hljs-type">path</span>.resolve(__dirname, <span class="hljs-string">&#x27;./src/index.ts&#x27;</span>),<br>  output: [<br>    &#123;<br>      file: <span class="hljs-type">path</span>.resolve(__dirname, <span class="hljs-string">&#x27;dist/umd/index.js&#x27;</span>),<br>      <span class="hljs-keyword">format</span>: <span class="hljs-string">&#x27;umd&#x27;</span>,<br>      // 这个<span class="hljs-type">name</span>属性非常重要，是通过 cdn 引入后，挂载到 <span class="hljs-keyword">window</span>上的属性名<br>      <span class="hljs-type">name</span>: <span class="hljs-string">&#x27;rain&#x27;</span><br>    &#125;<br>  ],<br>  plugins: [<br>    esbuild(&#123;<br>      // 为了应对 umd 直接加载到浏览器里，构建目标需要设定得兼容性更强<br>      target: <span class="hljs-string">&#x27;es2015&#x27;</span><br>    &#125;),<br>    // 需要babel plugin<br>    babel(&#123;<br>      presets: [<span class="hljs-string">&#x27;@babel/preset-env&#x27;</span>],<br>      <span class="hljs-keyword">exclude</span>: <span class="hljs-string">&#x27;node_modules/**&#x27;</span>,<br>      babelHelpers: <span class="hljs-string">&#x27;bundled&#x27;</span><br>    &#125;),<br>    nodeResolve(),<br>    <span class="hljs-type">json</span>(),<br>    commonjs()<br>  ],<br>&#125;;<br></code></pre></td></tr></table></figure><p>编写脚本：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs json">&#123;<br>  <span class="hljs-attr">&quot;scripts&quot;</span>: &#123;<br>    <span class="hljs-attr">&quot;build:umd&quot;</span>: <span class="hljs-string">&quot;rollup -c rollup.umd.config.js&quot;</span>,<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>执行脚本 <code>npm run build:umd</code> 即可完成 <code>umd</code> 格式的编译。</p><p>完成 <code>esm</code> 和 <code>umd</code> 的编译后，需要考虑一下：</p><p><strong>如何拥有一个良好的开发调试环境呢？</strong></p><h2 id="九、umd-构建物的调试"><a href="#九、umd-构建物的调试" class="headerlink" title="九、umd 构建物的调试"></a><strong>九、<code>umd</code> 构建物的调试</strong></h2><p><code>rollup</code> 的命令行支持参数 <code>-w</code> （和 <code>--watch</code> 等价），其含义是监听源文件的变动，当文件发生变化时立刻更新文件；</p><p>这听起来和 <code>webpack</code> 的 <code>hrm</code> 类似。</p><p>因此，<code>umd</code> 环境下构建物的调试思路便出来了：</p><ul><li>创建文件：<code>examples/base/index.html</code></li><li>在 <code>index.html</code>里引入 <code>dist/umd/index.js</code></li><li>起一个 <code>http</code> 静态服务以便访问 <code>index.html</code> 页面</li></ul><p>这就是最简单的前端调试逻辑。</p><p>此时，我们需要用到三个工具：</p><ol><li><code>cross-env</code> 工具库<br>通过 <code>cross-env</code>，我们可以轻易地跨平台地设置环境变量，比如:</li></ol><figure class="highlight mel"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs mel"><span class="hljs-keyword">cross</span>-<span class="hljs-keyword">env</span> NODE_ENV=production<br></code></pre></td></tr></table></figure><p>可以把 <code>process.env.NODE_ENV</code> 设置成 <code>production</code>，这对我们的区分 “调试” 与 “构建” 具有非常大的帮助，</p><ol><li><code>rollup-plugin-serve</code> 插件<br>这是一个 <code>rollup</code> 插件；<br>通过这个插件，你可以：</li></ol><ul><li>在固定端口启动 <code>http</code> 静态服务</li><li>指定该端口的 <code>root</code> 文件夹</li><li>指定浏览器默认打开页面，并可以指定直接访问某个路由</li></ul><ol><li><code>rollup-plugin-livereload</code> 插件<br>这也是一个 <code>rollup</code> 插件；<br>通过这个插件，可以监听某个文件夹，当里面的文件发生变动后，刷新页面。</li></ol><p>使用以上三个工具，可以写出如下代码：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs kotlin"><span class="hljs-keyword">const</span> isProduction = process.env.NODE_ENV === <span class="hljs-string">&#x27;production&#x27;</span><br><span class="hljs-keyword">const</span> pluginsWithEnv = isProduction ? [] : [serve(&#123;<br>  <span class="hljs-keyword">open</span>: <span class="hljs-literal">true</span>,<br>  openPage: <span class="hljs-string">&#x27;/base/&#x27;</span>,<br>  port: <span class="hljs-number">10001</span>,<br>  contentBase: [<span class="hljs-string">&#x27;dist&#x27;</span>, <span class="hljs-string">&#x27;examples&#x27;</span>]<br>&#125;), livereload(<span class="hljs-string">&#x27;dist/umd&#x27;</span>)]<br><br><span class="hljs-comment">// 然后通过 ...pluginsWithEnv 把配置结构到 rollup的plugins 里面去</span><br></code></pre></td></tr></table></figure><p>并添加脚本：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs json">&#123;<br>  <span class="hljs-attr">&quot;scripts&quot;</span>: &#123;<br>    <span class="hljs-attr">&quot;dev:umd&quot;</span>: <span class="hljs-string">&quot;cross-env NODE_ENV=development rollup -w -c rollup.umd.config.js&quot;</span><br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>完成以上步骤：执行 <code>npm run dev:umd</code> 就能正常打开页面，以及完成调试了。</p><h2 id="十、esm-构建物的调试"><a href="#十、esm-构建物的调试" class="headerlink" title="十、esm 构建物的调试"></a><strong>十、<code>esm</code> 构建物的调试</strong></h2><p>相比于 <code>umd</code> 那简单的调试结构，<code>esm</code> 通常情况下需要依赖构建工具，事情就变得复杂起来了。</p><p>我的选择是 <strong>在项目里内置一个基于 <code>webpack/vite</code> 的项目</strong> ；</p><h3 id="10-1-用-vue-cli-新增项目"><a href="#10-1-用-vue-cli-新增项目" class="headerlink" title="10.1 用 vue-cli 新增项目"></a>10.1 用 <code>vue-cli</code> 新增项目</h3><p>先安装 <code>vue-cli</code>，见 vue-cli 官网</p><p>然后执行以下命令：</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">cd</span> examples<br><span class="hljs-attribute">vue</span> create vue<span class="hljs-number">3</span><br></code></pre></td></tr></table></figure><h3 id="10-2-import-构建物"><a href="#10-2-import-构建物" class="headerlink" title="10.2 import 构建物"></a>10.2 <code>import</code> 构建物</h3><p>在 <code>vue3</code> 项目中通过相对路径引用 <code>esm</code> 构建物，引入方式如下：</p><figure class="highlight clean"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs clean"><span class="hljs-keyword">import</span> xx <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;../../../&#x27;</span><br></code></pre></td></tr></table></figure><p>注意，此处不需要指定到某个 <code>js</code> 文件，是因为要通过 <code>esm</code> 的导入方式，通过命中项目的 <code>package.json</code>，通过 <code>exports</code>、<code>main</code>、<code>module</code> 等属性命中它该命中的入口文件。</p><h3 id="10-3-编写脚本"><a href="#10-3-编写脚本" class="headerlink" title="10.3 编写脚本"></a>10.3 编写脚本</h3><p>编写如下脚本：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs json">&#123;<br>  <span class="hljs-attr">&quot;scripts&quot;</span>: &#123;<br>    <span class="hljs-attr">&quot;serve:vue3&quot;</span>: <span class="hljs-string">&quot;cd ./examples/vue3 &amp;&amp; yarn &amp;&amp; yarn serve&quot;</span>,<br>    <span class="hljs-attr">&quot;watch:esm&quot;</span>: <span class="hljs-string">&quot;cross-env NODE_ENV=development rollup -w -c rollup.esm.config.js&quot;</span>,<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>如果分别运行 <code>npn run watch:esm</code> 和 <code>npm run serve:vue3</code>，就能成功运行一个 <code>vue3</code> 项目，并成功进行调试。</p><h3 id="10-4-并联两个脚本"><a href="#10-4-并联两个脚本" class="headerlink" title="10.4 并联两个脚本"></a>10.4 并联两个脚本</h3><p>因为 <code>serve:vue3</code> 和 <code>watch:esm</code> 这两个脚本都是会导致命令行的命令，因此如果你这样写一个脚本：</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs routeros">npn <span class="hljs-builtin-name">run</span> watch:esm &amp;&amp; npm <span class="hljs-builtin-name">run</span> serve:vue3<br></code></pre></td></tr></table></figure><p>那么 <strong>后者永远不会执行</strong>。</p><p>因此，两者必须同时执行。</p><p>再推荐一个小工具：<code>npm-run-all</code> <a href="https://www.npmjs.com/package/npm-run-all">https://www.npmjs.com/package/npm-run-all</a></p><p>有了以上这个工具，你可以轻易的串行或者并行各种脚本。</p><p>此处只需要再新增一个脚本：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs json">&#123;<br>  <span class="hljs-attr">&quot;scripts&quot;</span>: &#123;<br>    <span class="hljs-attr">&quot;dev:esm&quot;</span>: <span class="hljs-string">&quot;run-p watch:esm serve:vue3&quot;</span>,<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>这样就可以异步同时执行两个脚本了。</p><h2 id="十一、开发-amp-发布"><a href="#十一、开发-amp-发布" class="headerlink" title="十一、开发 &amp; 发布"></a><strong>十一、开发 &amp; 发布</strong></h2><p>完成了以上所有步骤，你就可以开始安心开发脚本了，无论是 “构建” 抑或是 “调试”，目前都已经非常清晰了。</p><p>对于 <code>rollup.js</code> 在一个小型前端开源项目中应该如何使用，也渐渐摸清了脉络。</p><p>等开发完项目，则需要考虑发布的过程。</p><p>执行以下过程</p><figure class="highlight jboss-cli"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs jboss-cli">npm <span class="hljs-keyword">version</span> <span class="hljs-keyword">patch</span> <br></code></pre></td></tr></table></figure><p>项目编号会自动从 <code>0.0.1</code> 升级为 <code>0.0.2</code>。</p><p>再执行：</p><figure class="highlight coffeescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs coffeescript"><span class="hljs-built_in">npm</span> login<br><span class="hljs-built_in">npm</span> publish<br></code></pre></td></tr></table></figure><p>就能成功发布你的开源库了。</p><h2 id="十二、文章源码（供参考）"><a href="#十二、文章源码（供参考）" class="headerlink" title="十二、文章源码（供参考）"></a><strong>十二、文章源码（供参考）</strong></h2><p>参见：<a href="https://github.com/zhangshichun/rainy-window">https://github.com/zhangshichun/rainy-window</a></p>]]></content>
    
    
    
    <tags>
      
      <tag>Rollup</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>chrome高级调试技巧，学会效率直接提升</title>
    <link href="/blog/posts/2137c6ac.html"/>
    <url>/blog/posts/2137c6ac.html</url>
    
    <content type="html"><![CDATA[<div class="note note-success">            <p>本文转载，版权归原作者所有。</p> <p>本文作者：前端胖头鱼<br>原文地址：<a href="https://juejin.cn/post/7085135692568723492">https://juejin.cn/post/7085135692568723492</a></p>           </div><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>chrome浏览器作为前端童鞋的<code>老婆</code>，相信你一定不陌生。<code>调页面</code>、<code>写BUG</code>、<code>画样式</code>、<code>看php片</code>少了它整个世界都不香了。</p><p>不信？一起来看看我们的<code>老婆</code>有多厉害….</p><h2 id="1-一键重新发起请求"><a href="#1-一键重新发起请求" class="headerlink" title="1#. 一键重新发起请求"></a>1#. 一键重新发起请求</h2><p>在与后端接口联调或排查线上BUG时，你是不是也经常听到他们说这句话：<strong>你再发起一次请求试试，我这边看下为啥出错了！</strong></p><p>重发请求，这有一种简单到发指的方式。</p><ol><li>选中<code>Network</code></li><li>点击<code>Fetch/XHR</code></li><li>选择要重新发送的请求</li><li>右键选择<code>Replay XHR</code></li></ol><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e056245b2a9e4e6dbfb39db6903f9275~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp" alt="img"></p><p>不用刷新页面，不用走页面交互，是不是非常爽！！！</p><h2 id="2-在控制台快速发起请求"><a href="#2-在控制台快速发起请求" class="headerlink" title="2#. 在控制台快速发起请求"></a>2#. 在控制台快速发起请求</h2><p>还是联调或修BUG的场景，针对同样的请求，有时候需要<strong>修改入参</strong>重新发起，有啥快捷方式？</p><ol><li>选中<code>Network</code></li><li>点击<code>Fetch/XHR</code></li><li>选择<code>Copy as fetch</code></li><li>控制台粘贴代码</li><li>修改参数，回车搞定</li></ol><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f91af146bbee42cc9e99badf83de83a8~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp" alt="img"></p><p>曾经我总是通过改代码或者手写<code>fetch</code>的方式处理，想想真是太傻了…</p><h2 id="3-复制JavaScript变量"><a href="#3-复制JavaScript变量" class="headerlink" title="3#. 复制JavaScript变量"></a>3#. 复制JavaScript变量</h2><p>假如你的代码经过计算会输出一个<strong>复杂的对象</strong>，且需要被复制下来发送给其他人，怎么办？</p><ol><li>使用<code>copy</code>函数，将<code>对象</code>作为入参执行即可</li></ol><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8eac65d357c04a779149719621f477c0~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp" alt="img"></p><p>以前我总是通过<code>JSON.stringify(fetfishObj, null, 2)</code>打印到控制台，再手动复制粘贴，这效率实在是太低了…</p><h2 id="4-控制台获取Elements面板选中的元素"><a href="#4-控制台获取Elements面板选中的元素" class="headerlink" title="4#. 控制台获取Elements面板选中的元素"></a>4#. 控制台获取Elements面板选中的元素</h2><p>调试网页时通过<code>Elements</code>面板选中元素后，如果想通过<code>JS</code>知道它的一些属性，如<code>宽</code>、<code>高</code>、<code>位置</code>等怎么办呢？</p><ol><li>通过<code>Elements</code>选择要调试的元素</li><li>控制台直接用<code>$0</code>访问</li></ol><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4fd8a970c19842a7b73ee5d43f64efa6~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp" alt="img"></p><h2 id="5-截取一张全屏的网页"><a href="#5-截取一张全屏的网页" class="headerlink" title="5#. 截取一张全屏的网页"></a>5#. 截取一张全屏的网页</h2><p>偶尔咱们也会有对网页截屏的需求，一屏还好，系统自带的截屏或者微信截图等都可以办到，但是要求<strong>将超出一屏的内容也截下来咋办呢</strong>？</p><ol><li>准备好需要截屏的内容</li><li><code>cmd + shift + p</code> 执行<code>Command</code>命令</li><li>输入<code>Capture full size screenshot</code> 按下回车</li></ol><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/44643079db90418d8d359d4278605732~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp" alt="img"></p><p><strong>如果要截取选中的部分元素呢？</strong></p><p>答案也很简单，第三步输入<code>Capture node screenshot</code>即可</p><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3835874255224edbbd98977a1727ca7e~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp" alt="img"></p><h2 id="6-一键展开所有DOM元素"><a href="#6-一键展开所有DOM元素" class="headerlink" title="6#. 一键展开所有DOM元素"></a>6#. 一键展开所有DOM元素</h2><p>调试元素时，在层级比较深的情况下，你是不是也经常一个个展开去调试？有一种更加快捷的方式</p><ol><li>按住<code>opt</code>键 + click（需要展开的最外层元素）</li></ol><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c09ba071e1e34b9387ee0071905ad21a~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp" alt="img"></p><h2 id="7-控制台引用上一次执行的结果"><a href="#7-控制台引用上一次执行的结果" class="headerlink" title="7#. 控制台引用上一次执行的结果"></a>7#. 控制台引用上一次执行的结果</h2><p>来看看这个场景，我猜你也一定遇到过, 对某个字符串进行了各种工序，然后我们想知道每一步执行的结果，该咋办？。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-string">&#x27;fatfish&#x27;</span>.split(<span class="hljs-string">&#x27;&#x27;</span>).reverse().join(<span class="hljs-string">&#x27;&#x27;</span>) <span class="hljs-comment">// hsiftaf</span><br>复制代码<br></code></pre></td></tr></table></figure><p><strong>你可能会这样做</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">// 第1步</span><br><span class="hljs-string">&#x27;fatfish&#x27;</span>.split(<span class="hljs-string">&#x27;&#x27;</span>) <span class="hljs-comment">// [&#x27;f&#x27;, &#x27;a&#x27;, &#x27;t&#x27;, &#x27;f&#x27;, &#x27;i&#x27;, &#x27;s&#x27;, &#x27;h&#x27;]</span><br><span class="hljs-comment">// 第2步</span><br>[<span class="hljs-string">&#x27;f&#x27;</span>, <span class="hljs-string">&#x27;a&#x27;</span>, <span class="hljs-string">&#x27;t&#x27;</span>, <span class="hljs-string">&#x27;f&#x27;</span>, <span class="hljs-string">&#x27;i&#x27;</span>, <span class="hljs-string">&#x27;s&#x27;</span>, <span class="hljs-string">&#x27;h&#x27;</span>].reverse() <span class="hljs-comment">// [&#x27;h&#x27;, &#x27;s&#x27;, &#x27;i&#x27;, &#x27;f&#x27;, &#x27;t&#x27;, &#x27;a&#x27;, &#x27;f&#x27;]</span><br><span class="hljs-comment">// 第3步</span><br>[<span class="hljs-string">&#x27;h&#x27;</span>, <span class="hljs-string">&#x27;s&#x27;</span>, <span class="hljs-string">&#x27;i&#x27;</span>, <span class="hljs-string">&#x27;f&#x27;</span>, <span class="hljs-string">&#x27;t&#x27;</span>, <span class="hljs-string">&#x27;a&#x27;</span>, <span class="hljs-string">&#x27;f&#x27;</span>].join(<span class="hljs-string">&#x27;&#x27;</span>) <span class="hljs-comment">// hsiftaf</span><br>复制代码<br></code></pre></td></tr></table></figure><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/10903c73ac3945e39e820bdbef2be2a3~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp" alt="img"></p><p><strong>更简单的方式</strong></p><p>使用<code>$_</code>引用上一次操作的结果，不用每次都复制一遍</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">// 第1步</span><br><span class="hljs-string">&#x27;fatfish&#x27;</span>.split(<span class="hljs-string">&#x27;&#x27;</span>) <span class="hljs-comment">// [&#x27;f&#x27;, &#x27;a&#x27;, &#x27;t&#x27;, &#x27;f&#x27;, &#x27;i&#x27;, &#x27;s&#x27;, &#x27;h&#x27;]</span><br><span class="hljs-comment">// 第2步</span><br>$_.reverse() <span class="hljs-comment">// [&#x27;h&#x27;, &#x27;s&#x27;, &#x27;i&#x27;, &#x27;f&#x27;, &#x27;t&#x27;, &#x27;a&#x27;, &#x27;f&#x27;]</span><br><span class="hljs-comment">// 第3步</span><br>$_.join(<span class="hljs-string">&#x27;&#x27;</span>) <span class="hljs-comment">// hsiftaf</span><br>复制代码<br></code></pre></td></tr></table></figure><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b259e40d2d1e4d489058019617e7ac6c~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp" alt="img"></p><h2 id="8-快速切换主题"><a href="#8-快速切换主题" class="headerlink" title="8.# 快速切换主题"></a>8.# 快速切换主题</h2><p>有的同学喜欢chrome的白色主题，有的喜欢黑色，我们可以使用快捷键迅速切换两个主题。</p><ol><li><code>cmd + shift + p</code> 执行<code>Command</code>命令</li><li>输入<code>Switch to dark theme</code>或者<code>Switch to light theme</code>进行主题切换</li></ol><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d6626dd2efcf4fafb01fb354275c5c33~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp" alt="img"></p><h2 id="9-“-quot-和-quot-”选择器"><a href="#9-“-quot-和-quot-”选择器" class="headerlink" title="9.# “$$&quot;和&quot;$$$”选择器"></a>9.# “$$<code>&quot;和&quot;</code>$$$”选择器</h2><p>在控制台使用<code>document.querySelector</code>和<code>document.querySelectorAll</code>选择当前页面的元素是最常见的需求了，不过着实有点太长了，咱们可以使用$$<code>和</code>$$$替代。</p><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b99835a401524edeae0eebe599042df7~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp" alt="img"></p><h2 id="10-i直接在控制台安装npm包"><a href="#10-i直接在控制台安装npm包" class="headerlink" title="10.# $i直接在控制台安装npm包"></a>10.# <code>$i</code>直接在控制台安装npm包</h2><p>你遇到过这个场景吗？有时候想使用比如<code>dayjs</code>或者<code>lodash</code>的某个<code>API</code>，但是又不想去官网查，如果可以在控制台直接试出来就好了。</p><p><a href="https://link.juejin.cn?target=https%3A%2F%2Fchrome.google.com%2Fwebstore%2Fdetail%2Fconsole-importer%2Fhgajpakhafplebkdljleajgbpdmplhie%2Frelated">Console Importer</a> 就是这么一个插件，用来在控制台直接安装<code>npm</code>包。</p><ol><li>安装<code>Console Importer</code>插件</li><li>$i(‘name’)安装npm包</li></ol><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/980db6a2b2d74115bdab37e5b061a7a1~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp" alt="img"></p><h2 id="11-Add-conditional-breakpoint条件断点的妙用"><a href="#11-Add-conditional-breakpoint条件断点的妙用" class="headerlink" title="11.# Add conditional breakpoint条件断点的妙用"></a>11.# Add conditional breakpoint条件断点的妙用</h2><p>假设有下面这段代码，咱们希望食物名字是<code>🍫</code>时才触发断点，可以怎么弄？</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> foods = [<br>  &#123;<br>    <span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;🍔&#x27;</span>,<br>    <span class="hljs-attr">price</span>: <span class="hljs-number">10</span><br>  &#125;,<br>  &#123;<br>    <span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;🍫&#x27;</span>,<br>    <span class="hljs-attr">price</span>: <span class="hljs-number">15</span><br>  &#125;,<br>  &#123;<br>    <span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;🍵&#x27;</span>,<br>    <span class="hljs-attr">price</span>: <span class="hljs-number">20</span><br>  &#125;,<br>]<br><br>foods.forEach(<span class="hljs-function">(<span class="hljs-params">v</span>) =&gt;</span> &#123;<br>  <span class="hljs-built_in">console</span>.log(v.name, v.price)<br>&#125;)<br><br>复制代码<br></code></pre></td></tr></table></figure><p>这在大量数据下，只想对符合条件时打断点条件将会非常方便。试想如果没有条件断点咱们是不是要点n次debugger？</p><p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/43a4882a64374e3eb3e492380c248ae6~tplv-k3u1fbpfcp-zoom-in-crop-mark:1304:0:0:0.awebp" alt="img"></p><p>作者：前端胖头鱼<br>链接：<a href="https://juejin.cn/post/7085135692568723492">https://juejin.cn/post/7085135692568723492</a><br>来源：稀土掘金<br>著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。</p>]]></content>
    
    
    
    <tags>
      
      <tag>前端</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>在 Fluid 主题中加入谷歌广告</title>
    <link href="/blog/posts/8dc07bec.html"/>
    <url>/blog/posts/8dc07bec.html</url>
    
    <content type="html"><![CDATA[<div class="note note-success">            <p>本文由 Fluid 用户授权转载，版权归原作者所有。</p> <p>本文作者：张凯强</p> <p>原文地址：<a href="https://hexo.fluid-dev.com/posts/fluid-adsense/">https://hexo.fluid-dev.com/posts/fluid-adsense/</a></p>           </div><p>大家写博客都想给自己提供一些回报，挂 CRM 广告是最方便的方式之一 ，而<a href="https://www.google.com/adsense">谷歌广告</a>在所有 CRM 计费广告中是单价比较高的，不过就是提现比较麻烦，所以用之前最好先了解下，另外建议大家在博客有固定的流量后再加入广告，不然广告都是给自己看，可能会被谷歌判断刷量（谷歌对广告的规范很严格，放置过多的广告或者有诱导点击行为都是违规的）。</p><p>本文提供的方式需要 Fluid v1.9.0 以上版本。</p><p>然后在博客根目录下找到 <code>script</code> 文件夹（不存在就创建一个），进入后任意创建一个 js 文件，比如 <code>inject.js</code>，复制以下内容：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs javascript">hexo.extend.filter.register(<span class="hljs-string">&#x27;theme_inject&#x27;</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">injects</span>) </span>&#123;<br>injects.bodyEnd.raw(<span class="hljs-string">&#x27;adsense&#x27;</span>, <span class="hljs-string">&#x27;&lt;script async src=&quot;https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-xxxxxx&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;&#x27;</span>);<br>injects.head.raw(<span class="hljs-string">&#x27;adsense&#x27;</span>, <span class="hljs-string">&#x27;&lt;style&gt;ins.adsbygoogle[data-ad-status=&quot;unfilled&quot;] &#123; display: none !important; &#125;&lt;/style&gt;&#x27;</span>);<br>injects.postLeft.raw(<span class="hljs-string">&#x27;adsense&#x27;</span>, <span class="hljs-string">&#x27;&lt;aside class=&quot;sidebar d-none d-xl-block&quot; style=&quot;margin-right:-1rem;z-index:-1&quot;&gt;&lt;ins class=&quot;adsbygoogle&quot; style=&quot;display:flex;justify-content:center;min-width:160px;max-width:300px;width:100%;height:600px;position:sticky;top:2rem&quot; data-ad-client=&quot;ca-pub-xxxxxx&quot; data-ad-slot=&quot;yyyyyy&quot;&gt;&lt;/ins&gt;&lt;script&gt; (adsbygoogle = window.adsbygoogle || []).push(&#123;&#125;); &lt;/script&gt;&lt;/aside&gt;&#x27;</span>);<br>injects.postCopyright.raw(<span class="hljs-string">&#x27;adsense&#x27;</span>, <span class="hljs-string">&#x27;&lt;div style=&quot;width:100%;display:flex;justify-content:center;margin-bottom:1.5rem&quot;&gt;&lt;ins class=&quot;adsbygoogle&quot; style=&quot;display:flex;justify-content:center;max-width:845px;width:100%;height:90px&quot; data-ad-client=&quot;ca-pub-xxxxxx&quot; data-ad-slot=&quot;yyyyyy&quot;&gt;&lt;/ins&gt;&lt;script&gt; (adsbygoogle = window.adsbygoogle || []).push(&#123;&#125;); &lt;/script&gt;&lt;/div&gt;&#x27;</span>);<br>&#125;);<br></code></pre></td></tr></table></figure><p>以上代码的意思请参考 <a href="https://hexo.fluid-dev.com/docs/advance/#fluid-%E6%B3%A8%E5%85%A5%E4%BB%A3%E7%A0%81">Fluid 注入功能</a>，插入的广告位置和本页是一样的，分别是文章页的左侧和文末位置。</p><p>复制之后，把其中 <code>ca-pub-xxxxxx</code> 和 <code>data-ad-slot=&quot;yyyyyy&quot;</code> 按谷歌广告里提供的编号替换即可。</p><p>保存后就不需要其他额外的配置了，如果谷歌广告的配置没有问题的话，部署之后应该就能看到广告展示出来。</p>]]></content>
    
    
    <categories>
      
      <category>功能增强</category>
      
    </categories>
    
    
    <tags>
      
      <tag>花里胡哨</tag>
      
      <tag>Fluid</tag>
      
      <tag>转载</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>webGL编程指南总结</title>
    <link href="/blog/posts/3a1238a0.html"/>
    <url>/blog/posts/3a1238a0.html</url>
    
    <content type="html"><![CDATA[<p>个人计算机上使用最广泛的两种三维图形渲染技术是<strong>Direct3D</strong>和<strong>OpenGL</strong>。</p><h3 id="齐次坐标"><a href="#齐次坐标" class="headerlink" title="齐次坐标"></a>齐次坐标</h3><p>由4个分量组成的矢量被称为齐次坐标，齐次坐标(x,y,z,w)等价于三维坐标(x/w,y/w,z/w)，所以如果齐次坐标的第4个分量是1，那么就可以将它当作三维坐标来使用。w的值必须是大于等于0的，如果w趋近于0，那么它所表示的点将趋近于无穷远，所以在齐次坐标系统中可以有无穷远的概念，齐次坐标的存在，使得矩阵乘法来描述顶点成为可能，三维图形系统中，通常使用齐次坐标来表示顶点的三维坐标。</p><h2 id="着色器"><a href="#着色器" class="headerlink" title="着色器"></a>着色器</h2><h3 id="顶点着色器"><a href="#顶点着色器" class="headerlink" title="顶点着色器"></a>顶点着色器</h3><p>顶点着色器控制点的位置和大小</p><h3 id="片元着色器"><a href="#片元着色器" class="headerlink" title="片元着色器"></a>片元着色器</h3><p>片元着色器控制点的颜色，简单的讲，片元就是显示在屏幕上的一个像素（严格意义上的来说，片元包括这个像素的位置、颜色和其他信息）。</p><p>一旦顶点着色器执行完后，片元着色器就会开始执行，调用main()函数。</p><h2 id="坐标系统"><a href="#坐标系统" class="headerlink" title="坐标系统"></a>坐标系统</h2><p>三维坐标是笛卡尔坐标系统，具有X、Y、Z轴，通常，在webGL中，当你面向计算机，X轴方向是水平的(正方向是向右👉)，Y轴是垂直的(正方向是向下👇)，Z轴是垂直于屏幕的(正方向是向外)。这套坐标系又被称为右手坐标系，默认情况下WebGL使用右手坐标系，实际上WebGL既不是右手坐标系也不是左手坐标系。</p><h2 id="attribute变量"><a href="#attribute变量" class="headerlink" title="attribute变量"></a>attribute变量</h2><p>我们的目标是将信息从JavaScript程序中传给顶点着色器，有两种方式可以做到:<strong>attribute变量</strong>和<strong>uniform变量</strong>。使用哪个变量取决于需要传输的数据本身，<strong>attribute变量</strong>传输的是那些与顶点相关的数据，而<strong>uniform变量</strong>传输的那些对于所有顶点都相同(或与顶点无关)的数据。关键词<strong>attribute</strong>被称为存储限定符</p>]]></content>
    
    
    
    <tags>
      
      <tag>WebGL</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>JavaScript整理之从原型到原型链</title>
    <link href="/blog/posts/ba2c7364.html"/>
    <url>/blog/posts/ba2c7364.html</url>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>先直接上一个原型链图以便讲解:</p><img src="/blog/posts/ba2c7364/1865431362-c3d090d5d00755cf_fix732-1625456575241-16308127777682.png" class="" title="JavaScript原型和原型链"><p>图中Parent是构造函数，p1是通过Parent实例化出来的一个对象。</p><p>想要弄清楚原型和原型链，这几个属性必须要搞清楚，<code>__proto__</code>、<code>prototype</code>、 <code>constructor</code>。JavaScript中一切皆对象，函数也是对象。函数中有个特殊的函数——构造函数，任何函数都可以作为构造函数，但是并不能将任意函数叫做构造函数，只有当一个函数通过new关键字调用的时候才可以成为构造函数。下面几点是理解原型和原型链的重要知识点：</p><ul><li><code>__proto__</code>、 <code>constructor</code>属性是对象所独有的；</li><li><code>prototype</code>属性是函数独有的；</li><li>上面说过JavaScript中函数也是对象的一种，那么函数同样也有属性<code>__proto__</code>、 <code>constructor</code>；</li></ul><h2 id="prototype属性"><a href="#prototype属性" class="headerlink" title="prototype属性"></a>prototype属性</h2><p>它是函数独有的属性，从图中可以看到它从一个函数指向另一个对象，代表这个对象是这个函数的原型对象，这个对象也是当前函数所创建的实例的原型对象。<br><code>prototype</code>设计之初就是为了实现继承，让由特定函数创建的所有实例共享属性和方法，也可以说是让某一个构造函数实例化的所有对象可以找到公共的方法和属性。有了<code>prototype</code>我们不需要为每一个实例创建重复的属性方法，而是将属性方法创建在构造函数的原型对象上（prototype）。那些不需要共享的才创建在构造函数中。</p><img src="/blog/posts/ba2c7364/323548351-1642fae65598fc65-16311798697453-16311798757894.png" class="" title="323548351-1642fae65598fc65"><p>继续引用上面的代码，当我们想为通过Parent实例化的所有实例添加一个共享的属性时</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">Parent.prototype.name = <span class="hljs-string">&quot;prototype&quot;</span>;<br></code></pre></td></tr></table></figure><p>这就是原型属性，当然你也可以添加原型方法。</p><h2 id="proto-属性"><a href="#proto-属性" class="headerlink" title="__proto__属性"></a><code>__proto__</code>属性</h2><p><code>__proto__</code>属性是对象（包括函数）就有的属性。从图中可以看到<strong><code>__proto__</code>属性</strong>是从一个对象指向另一个对象，即<strong>从一个对象指向该对象的原型对象</strong>。显然它的含义就是告诉我们一个对象的原型对象是谁。<br>prototype篇章我们说到，<code>Parent.prototype</code>上添加的属性和方法叫做原型属性和原型方法，该构造函数的实例都可以访问调用。那这个构造函数的原型对象上的属性和方法，怎么能和构造函数的实例联系在一起呢，就是通过<code>__proto__</code>属性。每个对象都有<code>__proto__</code>属性，该属性指向的就是该对象的原型对象。</p><img src="/blog/posts/ba2c7364/2678004328-ae93e0e517266b2a_fix732-16311800444655.png" class="" title="2678004328-ae93e0e517266b2a_fix732"><p><code>__proto__</code>通常称为隐式原型，<code>prototype</code>通常称为显式原型，那我们可以说一个对象的隐式原型指向了该对象的构造函数的显式原型。那么我们在显式原型上定义的属性方法，通过隐式原型传递给了构造函数的实例。这样一来实例就能很容易的访问到构造函数原型上的方法和属性了。<br>我们之前也说过<code>__proto__</code>属性是对象（包括函数）独有的，那么<code>Parent.prototype</code>也是对象，那它有隐式原型么？又指向谁？</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">Parent.prototype.__proto__ === <span class="hljs-built_in">Object</span>.prototype; <span class="hljs-comment">//true</span><br></code></pre></td></tr></table></figure><p>可以看到，构造函数的原型对象上的隐式原型对象指向了Object的原型对象。那么Parent的原型对象就继承了Object的原型对象。由此我们可以验证一个结论，万物继承自Object.prototype。这也就是为什么我们可以实例化一个对象，并且可以调用该对象上没有的属性和方法了。如：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">//我们并没有在Parent中定义任何方法属性，但是我们可以调用</span><br>p1.toString();<span class="hljs-comment">//hasOwnProperty 等等的一些方法</span><br></code></pre></td></tr></table></figure><p>我们可以调用很多我们没有定义的方法，这些方法是哪来的呢？现在引出原型链的概念，当我们调用<code>p1.toString()</code>的时候，先在<code>p1</code>对象本身寻找，没有找到则通过<code>p1.__proto__</code>找到了原型对象<code>Parent.prototype</code>，也没有找到，又通过<code>Parent.prototype.__proto__</code>找到了上一层原型对象<code>Object.prototype</code>。在这一层找到了<code>toString</code>方法。返回该方法供<code>p1</code>使用。<br>当然如果找到<code>Object.prototype</code>上也没找到，就在<code>Object.prototype.__proto__</code>中寻找，但是<code>Object.prototype.__proto__ === null</code>所以就返回<code>undefined</code>。这就是为什么当访问对象中一个不存在的属性时，返回<code>undefined</code>了。</p><h2 id="constructor属性"><a href="#constructor属性" class="headerlink" title="constructor属性"></a>constructor属性</h2><img src="/blog/posts/ba2c7364/48568374-4a2ca8b9a839496e_fix732-16311803210376.png" class="" title="48568374-4a2ca8b9a839496e_fix732"><p>constructor是对象才有的属性，从图中看到它是从一个对象指向一个函数的。指向的函数就是该对象的构造函数。每个对象都有构造函数，好比我们上面的代码<code>p1</code>就是一个对象，那<code>p1</code>的构造函数是谁呢？我们打印一下。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-built_in">console</span>.log(p1.constructor); <span class="hljs-comment">// ƒ Parent()&#123;&#125;</span><br></code></pre></td></tr></table></figure><p>通过输出结果看到，很显然是Parent函数。我们有说过函数也是对象，那Parent函数是不是也有构造函数呢？显然是有的。再次打印下。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-built_in">console</span>.log(Parent.constructor); <span class="hljs-comment">// ƒ Function() &#123; [native code] &#125;</span><br></code></pre></td></tr></table></figure><p>通过输出看到Parent函数的构造函数是Function()，这点也不奇怪，因为我们每次定义函数其实都是调用了new Function()，下面两种效果是一样的。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">var</span> fn1 = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Function</span>(<span class="hljs-string">&#x27;msg&#x27;</span>,<span class="hljs-string">&#x27;alert(msg)&#x27;</span>);<br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fn1</span>(<span class="hljs-params">msg</span>)</span>&#123;<br>    alert(msg);<br>&#125;<br></code></pre></td></tr></table></figure><p>那么我们再回来看下，再次打印<code>Function.constructor</code></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">Function</span>.constructor); <span class="hljs-comment">// ƒ Function() &#123; [native code] &#125;</span><br><span class="hljs-built_in">Function</span>.constructor===<span class="hljs-built_in">Function</span> <span class="hljs-comment">//true</span><br></code></pre></td></tr></table></figure><p>可以看到Function函数的构造函数就是本身了，那我们也就可以说Function是所有函数的根构造函数。<br>到这里我们已经对constructor属性有了一个初步的认识，它的作用是从一个对象指向一个函数，这个函数就是该对象的构造函数。通过栗子我们可以看到，<code>p1</code>的<code>constructor</code>属性指向了<code>Parent</code>，那么<code>Parent</code>就是<code>p1</code>的构造函数。同样<code>Parent</code>的<code>constructor</code>属性指向了<code>Function</code>，那么<code>Function</code>就是<code>Parent</code>的构造函数，然后又验证了<code>Function</code>就是根构造函数。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><img src="/blog/posts/ba2c7364/image-20210909175104199-16311810670437.png" class="" title="image-20210909175104199"><img src="/blog/posts/ba2c7364/image-20210909175134628-16311810962428.png" class="" title="image-20210909175134628">]]></content>
    
    
    
    <tags>
      
      <tag>JavaScript</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>使用Rollup创作一个可以随处使用的JavaScript库</title>
    <link href="/blog/posts/90da98cf.html"/>
    <url>/blog/posts/90da98cf.html</url>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在本文中，我们的目标是创建和发布一个无需更改代码即可在客户端和服务器端应用程序中使用的库。</p><p>我们需要满足以下用例：</p><ol><li>该库是用 ES6+ 编写的，使用 import 和 export 关键字</li><li>该库可以与 <code>&lt;script&gt;</code>标签一起使用</li><li>该库可用于使用现代打包器的 Web 应用程序。</li><li>该库可用于Node应用程序。</li></ol><p>从技术上讲，这意味着库需要在以下上下文中工作：</p><p>使用<code>&lt;script&gt;</code>标签：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;scripts/my-library.min.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;app&quot;</span> /&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><br>      myLibrary.helloWorld();<br>    <span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span><br></code></pre></td></tr></table></figure><p>使用 RequireJS：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js">define([<span class="hljs-string">&quot;my-library&quot;</span>], <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">myLibrary</span>) </span>&#123;&#125;);<br><span class="hljs-comment">// or</span><br>define(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"><span class="hljs-built_in">require</span></span>) </span>&#123;<br>  <span class="hljs-keyword">var</span> myLibrary = <span class="hljs-built_in">require</span>(<span class="hljs-string">&quot;my-library&quot;</span>);<br>&#125;);<br></code></pre></td></tr></table></figure><p>在使用诸如 webpack 之类的打包器的 Web 应用程序中：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> &#123; helloWorld &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;my-library&quot;</span>;<br>helloWorld();<br></code></pre></td></tr></table></figure><p>在Node的应用程序中：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> myLibrary = <span class="hljs-built_in">require</span>(<span class="hljs-string">&quot;my-library&quot;</span>);<br>myLibrary.helloWorld();<br><span class="hljs-comment">// or</span><br><span class="hljs-keyword">const</span> &#123; helloWorld &#125; = <span class="hljs-built_in">require</span>(<span class="hljs-string">&quot;my-library&quot;</span>);<br>helloWorld();<br></code></pre></td></tr></table></figure><p><strong>注：</strong>在Web应用程序中我们使用打包工具，没有办法导入整个库，并调用它(<code>import lib from &#39;library&#39;; lib.sayHello();</code>)。我们希望调用他们使用时tree-shaking 就可以完成它的工作，并在打包最终应用程序时消除死代码。</p><p>为了实现这一切，我们将使用<a href="https://rollupjs.org/">rollup.js</a>。主要原因是 Rollup 非常快（<a href="https://github.com/evanw/esbuild">虽然不是最快的</a>），需要最少的配置，并通过其方便的插件系统支持我们需要的一切。</p><h2 id="什么是rollup？"><a href="#什么是rollup？" class="headerlink" title="什么是rollup？"></a>什么是<code>rollup</code>？</h2><p>系统的了解<code>rollup</code>之前，我们先来简单了解下<code>What is rollup？</code></p><p>关于<code>rollup</code>的介绍，官方文档已经写的很清楚了：</p><blockquote><p>Rollup 是一个 JavaScript 模块打包器，可以将小块代码编译成大块复杂的代码，例如 library 或应用程序。</p></blockquote><p><strong><code>Webpack</code>偏向于应用打包的定位不同，<code>rollup.js</code>更专注于<code>Javascript</code>类库打包。</strong></p><p>我们熟知的<code>Vue</code>、<code>React</code>等诸多知名框架或类库都是通过<code>rollup.js</code>进行打包。</p><h2 id="为什么是rollup"><a href="#为什么是rollup" class="headerlink" title="为什么是rollup"></a>为什么是<code>rollup</code></h2><p><code>webpack</code>我相信做前端的同学大家都用过，那么为什么有些场景还要使用<code>rollup</code>呢？这里我简单对<code>webpack</code>和<code>rollup</code>做一个比较：</p><p>总体来说<code>webpack</code>和<code>rollup</code>在不同场景下，都能发挥自身优势作用。<code>webpack</code>对于代码分割和静态资源导入有着“先天优势”，并且支持热模块替换(<code>HMR</code>)，而<code>rollup</code>并不支持。</p><p>所以当开发应用时可以优先选择<code>webpack</code>，但是<code>rollup</code>对于代码的<code>Tree-shaking</code>和<code>ES6</code>模块有着算法优势上的支持，若你项目只需要打包出一个简单的<code>bundle</code>包，并是基于<code>ES6</code>模块开发的，可以考虑使用<code>rollup</code>。</p><p>其实<code>webpack</code>从<code>2.0</code>开始就已经支持<code>Tree-shaking</code>，并在使用<code>babel-loader</code>的情况下还可以支持<code>es6 module</code>的打包。实际上，<code>rollup</code>已经在渐渐地失去了当初的优势了。但是它并没有被抛弃，反而因其简单的<code>API</code>、使用方式被许多库开发者青睐，如<code>React</code>、<code>Vue</code>等，都是使用<code>rollup</code>作为构建工具的。</p><h2 id="使用rollup"><a href="#使用rollup" class="headerlink" title="使用rollup"></a>使用rollup</h2><p>编写完我们的库后，我们将使用 Rollup 将代码导出为以下三种格式：</p><ol><li>UMD（通用模块定义）：这将支持使用脚本标签和 RequireJS。由于使用应用程序不会自己转译或打包代码，我们需要提供一个版本的库，该版本经过缩小和转译以获得广泛的浏览器支持。</li><li>ESM（ES2015 模块）：这将允许打包器导入我们的应用程序，消除死代码并将其转换为他们选择的级别。我们仍编译代码，但只是以方便调用者的格式，让他们决定下一步做什么。这将允许<code>import</code>关键字工作。</li><li>CJS (CommonJS)：<a href="https://nodejs.org/">Node.js</a>的首选格式。这里不需要<code>Tree-shaking</code>，因为代码大小无关紧要，这种格式允许<code>require</code>在节点应用程序中使用关键字。</li></ol><p>对于这些格式中的每一种，我们还将提供一个源映射，以便调用时可以在需要时调试库。</p><p>第一步是创建一个项目：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs sh">$ mkdir my-library <br>$ <span class="hljs-built_in">cd</span> my-library <br>$ npm init -y<br></code></pre></td></tr></table></figure><p>接下来我们需要添加一些依赖项。</p><p>显然，我们需要汇总。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sh">$ npm install rollup --save-dev<br></code></pre></td></tr></table></figure><p>我们知道我们需要转译 UMD 格式的代码，所以让我们安装 babel：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sh">$ npm install @babel/core @babel/preset-env --save-dev<br></code></pre></td></tr></table></figure><p>我们还需要 rollup 来使用 babel 并缩小代码，所以让我们安装必要的插件来使用 babel 和<a href="https://terser.org/">terser</a>：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sh">$ npm install @rollup/plugin-babel rollup-plugin-terser --save-dev<br></code></pre></td></tr></table></figure><p>最后，我们希望能够以<a href="https://nodejs.org/api/modules.html#modules_all_together">node</a>的风格在我们的库中使用导入/导出语法：这允许使用 write<code>import fn from &#39;./fn&#39;</code>而不是<code>import fn from &#39;./fn/index.js&#39;</code>并且当然可以使用 node_modules 目录中的模块（我们在这里没有这样做）。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sh">$ npm install @rollup/plugin-node-resolve --save-dev<br></code></pre></td></tr></table></figure><p>我们库的最终依赖列表应该是这样的：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-string">&quot;dependencies&quot;</span>: &#123;&#125;, <br><span class="hljs-string">&quot;devDependencies&quot;</span>: &#123; <br>  <span class="hljs-attr">&quot;@babel/core&quot;</span>: <span class="hljs-string">&quot;^7.11.6&quot;</span>, <br>  <span class="hljs-attr">&quot;@babel/preset-env&quot;</span>: <span class="hljs-string">&quot;^7.11.5&quot;</span>, <br>  <span class="hljs-attr">&quot;@rollup/plugin-babel&quot;</span> : <span class="hljs-string">&quot;^5.2.1&quot;</span>, <br>  <span class="hljs-attr">&quot;@rollup/plugin-node-resolve&quot;</span>: <span class="hljs-string">&quot;^9.0.0&quot;</span>, <br>  <span class="hljs-attr">&quot;rollup&quot;</span>: <span class="hljs-string">&quot;^2.28.2&quot;</span>, <br>  <span class="hljs-attr">&quot;rollup-plugin-terser&quot;</span>: <span class="hljs-string">&quot;^7.0.2 &quot;</span> <br>&#125;,<br></code></pre></td></tr></table></figure><p>我们还需要一个存放源代码的目录、一个 babel 的配置文件和一个 rollup 的配置文件：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs json">$ mkdir src <br>$ touch .babelrc.json <span class="hljs-comment">// 或者新建.babelrc文件，二者选其一</span><br>$ touch rollup.config.js<br></code></pre></td></tr></table></figure><p>babel 的配置将非常简单，我们只需要告诉 babel 我们要使用最新版本的 JavaScript：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs json">&#123;<br>  <span class="hljs-attr">&quot;presets&quot;</span>: [[<span class="hljs-string">&quot;@babel/env&quot;</span>, &#123; <span class="hljs-attr">&quot;modules&quot;</span>: <span class="hljs-literal">false</span> &#125;]]<br>&#125;<br></code></pre></td></tr></table></figure><p>对于 Rollup，我们需要导入必要的插件：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> &#123; nodeResolve &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@rollup/plugin-node-resolve&quot;</span>;<br><span class="hljs-keyword">import</span> &#123; terser &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;rollup-plugin-terser&quot;</span>;<br><span class="hljs-keyword">import</span> babel <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@rollup/plugin-babel&quot;</span>;<br></code></pre></td></tr></table></figure><p>我们还将导入 package.json，因此我们可以<code>name</code>在导出 UMD 包时使用该字段：</p><figure class="highlight capnproto"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs capnproto"><span class="hljs-keyword">import</span> pkg <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;./package.json&quot;</span>;<br></code></pre></td></tr></table></figure><p>我们<code>rollup.config.js</code>要做两件事：</p><p>对于 UMD：获取代码，处理它并通过 babel（transpile）和 terser（minify）运行它，并将其导出为 UMD 可消耗文件。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs js">&#123;<br>  <span class="hljs-comment">// UMD</span><br>  <span class="hljs-attr">input</span>: <span class="hljs-string">&quot;src/index.js&quot;</span>,<br>  <span class="hljs-attr">plugins</span>: [<br>    nodeResolve(),<br>    babel(&#123;<br>      <span class="hljs-attr">babelHelpers</span>: <span class="hljs-string">&quot;bundled&quot;</span>,<br>    &#125;),<br>    terser(),<br>  ],<br>  <span class="hljs-attr">output</span>: &#123;<br>    <span class="hljs-attr">file</span>: <span class="hljs-string">`dist/<span class="hljs-subst">$&#123;pkg.name&#125;</span>.min.js`</span>,<br>    <span class="hljs-attr">format</span>: <span class="hljs-string">&quot;umd&quot;</span>,<br>    <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;myLibrary&quot;</span>,<br>    <span class="hljs-attr">esModule</span>: <span class="hljs-literal">false</span>,<br>    <span class="hljs-attr">exports</span>: <span class="hljs-string">&quot;named&quot;</span>,<br>    <span class="hljs-attr">sourcemap</span>: <span class="hljs-literal">true</span>,<br>  &#125;,<br>&#125;,<br></code></pre></td></tr></table></figure><p>对于 CJS/ESM：获取代码，对其进行处理，然后将其导出为 ESM 模块和 CJS 模块。请记住，在这种情况下，我们不需要转译或缩小。Node 不需要它，而对于 ESM，调用者会这样做。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs js">&#123;<br>  <span class="hljs-attr">input</span>: [<span class="hljs-string">&quot;src/index.js&quot;</span>],<br>  <span class="hljs-attr">plugins</span>: [nodeResolve()],<br>  <span class="hljs-attr">output</span>: [<br>    &#123;<br>      <span class="hljs-attr">dir</span>: <span class="hljs-string">&quot;dist/esm&quot;</span>,<br>      <span class="hljs-attr">format</span>: <span class="hljs-string">&quot;esm&quot;</span>,<br>      <span class="hljs-attr">exports</span>: <span class="hljs-string">&quot;named&quot;</span>,<br>      <span class="hljs-attr">sourcemap</span>: <span class="hljs-literal">true</span>,<br>    &#125;,<br>    &#123;<br>      <span class="hljs-attr">dir</span>: <span class="hljs-string">&quot;dist/cjs&quot;</span>,<br>      <span class="hljs-attr">format</span>: <span class="hljs-string">&quot;cjs&quot;</span>,<br>      <span class="hljs-attr">exports</span>: <span class="hljs-string">&quot;named&quot;</span>,<br>      <span class="hljs-attr">sourcemap</span>: <span class="hljs-literal">true</span>,<br>    &#125;,<br>  ],<br>&#125;,<br></code></pre></td></tr></table></figure><p>然而，在所有情况下，我们都会生成一个源映射。</p><p>注意<code>exports: &quot;named&quot;</code>所有配置中的选项，<a href="https://rollupjs.org/guide/en/#outputexports">在 rollup 的文档中有更好的解释</a>，基本上这告诉 rollup 我们使用命名导出而不是默认导出。长话短说，这允许尽可能广泛的兼容性，并使<code>Tree-shaking</code>发生。如果您使用 linter，请确保将其配置为支持命名导出而不是默认导出（这不适用于应用程序，仅适用于库，使用默认导出甚至混合应用程序的默认/命名导出完全没问题）。</p><p>完整的汇总文件如下所示。由于名称取自 package.json，您实际上几乎可以按原样使用此文件，只要入口点是，<code>src/index.js</code>并且<code>name</code>在 UMD 模块的输出中进行了相应设置。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> &#123; nodeResolve &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@rollup/plugin-node-resolve&quot;</span>;<br><span class="hljs-keyword">import</span> &#123; terser &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;rollup-plugin-terser&quot;</span>;<br><span class="hljs-keyword">import</span> babel <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@rollup/plugin-babel&quot;</span>;<br><span class="hljs-keyword">import</span> pkg <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;./package.json&quot;</span>;<br><span class="hljs-keyword">const</span> input = [<span class="hljs-string">&quot;src/index.js&quot;</span>];<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> [<br>  &#123;<br>    <span class="hljs-comment">// UMD</span><br>    input,<br>    <span class="hljs-attr">plugins</span>: [<br>      nodeResolve(),<br>      babel(&#123;<br>        <span class="hljs-attr">babelHelpers</span>: <span class="hljs-string">&quot;bundled&quot;</span>,<br>      &#125;),<br>      terser(),<br>    ],<br>    <span class="hljs-attr">output</span>: &#123;<br>      <span class="hljs-attr">file</span>: <span class="hljs-string">`dist/<span class="hljs-subst">$&#123;pkg.name&#125;</span>.min.js`</span>,<br>      <span class="hljs-attr">format</span>: <span class="hljs-string">&quot;umd&quot;</span>,<br>      <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;myLibrary&quot;</span>, <span class="hljs-comment">// this is the name of the global object</span><br>      <span class="hljs-attr">esModule</span>: <span class="hljs-literal">false</span>,<br>      <span class="hljs-attr">exports</span>: <span class="hljs-string">&quot;named&quot;</span>,<br>      <span class="hljs-attr">sourcemap</span>: <span class="hljs-literal">true</span>,<br>    &#125;,<br>  &#125;,<br><span class="hljs-comment">// ESM and CJS</span><br>  &#123;<br>    input,<br>    <span class="hljs-attr">plugins</span>: [nodeResolve()],<br>    <span class="hljs-attr">output</span>: [<br>      &#123;<br>        <span class="hljs-attr">dir</span>: <span class="hljs-string">&quot;dist/esm&quot;</span>,<br>        <span class="hljs-attr">format</span>: <span class="hljs-string">&quot;esm&quot;</span>,<br>        <span class="hljs-attr">exports</span>: <span class="hljs-string">&quot;named&quot;</span>,<br>        <span class="hljs-attr">sourcemap</span>: <span class="hljs-literal">true</span>,<br>      &#125;,<br>      &#123;<br>        <span class="hljs-attr">dir</span>: <span class="hljs-string">&quot;dist/cjs&quot;</span>,<br>        <span class="hljs-attr">format</span>: <span class="hljs-string">&quot;cjs&quot;</span>,<br>        <span class="hljs-attr">exports</span>: <span class="hljs-string">&quot;named&quot;</span>,<br>        <span class="hljs-attr">sourcemap</span>: <span class="hljs-literal">true</span>,<br>      &#125;,<br>    ],<br>  &#125;,<br>];<br></code></pre></td></tr></table></figure><p>现在我们有了依赖项，配置了 babel 和 rollup，是时候编写代码了。</p><p>我们将像这样布局我们的文件：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs sh">src<br>├── goodbye<br>│   ├── goodbye.js<br>│   └── index.js<br>├── hello<br>│   ├── hello.js<br>│   └── index.js<br>└── index.js<br></code></pre></td></tr></table></figure><p>代码将非常简单：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// src/index.js</span><br><span class="hljs-keyword">export</span> &#123; <span class="hljs-keyword">default</span> <span class="hljs-keyword">as</span> hello &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;./hello&quot;</span>;<br><span class="hljs-keyword">export</span> &#123; <span class="hljs-keyword">default</span> <span class="hljs-keyword">as</span> goodbye &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;./goodbye&quot;</span>;<br><span class="hljs-comment">// src/hello/index.js</span><br><span class="hljs-keyword">export</span> &#123; <span class="hljs-keyword">default</span> &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;./hello&quot;</span>;<br><span class="hljs-comment">// src/hello/hello.js</span><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hello</span>(<span class="hljs-params"></span>) </span>&#123;<br>  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;hello&quot;</span>);<br>&#125;<br><span class="hljs-comment">// src/goodbye/index.js</span><br><span class="hljs-keyword">export</span> &#123; <span class="hljs-keyword">default</span> &#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;./goodbye&quot;</span>;<br><span class="hljs-comment">// src/goodbye/goodbye.js</span><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">goodbye</span>(<span class="hljs-params"></span>) </span>&#123;<br>  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;goodbye&quot;</span>);<br>&#125;<br></code></pre></td></tr></table></figure><p>接下来我们需要调用 rollup 并告诉它完成它的工作。为方便起见，我们将创建两个 npm 脚本，一个用于构建库，一个开发任务将在每次更改时重新编译代码：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs json">“scripts”: &#123;<br> “build”: “rollup -c”,<br> “dev”: “rollup -c -w”<br>&#125;,<br></code></pre></td></tr></table></figure><p>最后，我们需要描述应用程序是如何导出的，既可以让 npmjs 使用，也可以让调用者使用。</p><p>我们将在 package.json 中定义三个值：</p><p>files 选项告诉 npm 要打包什么（这可以使用 npm pack 进行测试），指向 CJS 模块的主要选项，以及 module 选项，虽然不是标准的，但已成为 ESM 模块的规范。</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-comment">// package.json</span><br>...<br><span class="hljs-string">&quot;main&quot;</span>: <span class="hljs-string">&quot;dist/cjs/index.js&quot;</span>,<br><span class="hljs-string">&quot;module&quot;</span>: <span class="hljs-string">&quot;dist/esm/index.js&quot;</span>,<br>...<br>files: [<br>  <span class="hljs-string">&quot;dist&quot;</span><br>]<br></code></pre></td></tr></table></figure><p>就是这样！</p><p>要构建库，只需运行<code>npm run build</code>，在开发过程中，您可以使用<code>npm run dev</code>. 可以使用导出来测试<code>npm pack</code></p><h2 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h2><p><strong>使用 script 标签</strong>，只需创建一个 HTML 文件，然后在浏览器中打开它。您将在控制台中看到“hello”这个词。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;dist/my-library.min.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><br>      myLibrary.hello()<br>    <span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span><br></code></pre></td></tr></table></figure><p><strong>使用 Requires.JS</strong>，创建一个小的 webapp 并使用<a href="https://www.npmjs.com/package/serve">serve 为其提供服务</a>：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs sh">www<br>├── index.html<br>└── scripts<br>    ├── app.js<br>    ├── my-library.min.js<br>    └── require.js<br></code></pre></td></tr></table></figure><p>index.html</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span></span><br><span class="hljs-tag">      <span class="hljs-attr">data-main</span>=<span class="hljs-string">&quot;scripts/app.js&quot;</span></span><br><span class="hljs-tag">      <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;scripts/require.js&quot;</span></span><br><span class="hljs-tag">    &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span><br></code></pre></td></tr></table></figure><p>app.js</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs js">requirejs.config(&#123;<br>  <span class="hljs-attr">baseUrl</span>: <span class="hljs-string">&quot;scripts&quot;</span><br>&#125;);<br>requirejs([<span class="hljs-string">&quot;my-library.min&quot;</span>], <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">myLibrary</span>) </span>&#123;<br>  myLibrary.hello();<br>&#125;);<br></code></pre></td></tr></table></figure><p>“hello”这个词将打印在控制台中。模块发布后，<code>my-library.min.js</code>可从<a href="https://unpkg.com/获得该文件。">https://unpkg.com/获得该文件。</a></p><p><strong>从 node</strong>，在 library 目录之外，创建一个 js 文件并通过指向 my-library 目录（不是 dist 文件夹！）来要求模块：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> myLibrary = <span class="hljs-built_in">require</span>(<span class="hljs-string">&quot;../my-library&quot;</span>);<br>myLibrary.hello(); <span class="hljs-comment">// hello</span><br>myLibrary.goodbye(); <span class="hljs-comment">// goodbye</span><br></code></pre></td></tr></table></figure><p>如果您更进一步并调试应用程序，源映射也将启动！</p><p><strong>从使用 webpack 的 Web 应用程序</strong>，如 React 应用程序：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sh">$ npx create-react-app my-library-cra <br>$ <span class="hljs-built_in">cd</span> my-library-cra<br></code></pre></td></tr></table></figure><p>在 package.json 的依赖项部分，只需添加以下行：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-string">&quot;my-library&quot;</span>: <span class="hljs-string">&quot;../my-library/&quot;</span><br></code></pre></td></tr></table></figure><p>并运行 <code>yarn install</code></p><p>在src/App.js，进口和调用Hello功能<strong>只需要</strong>：</p><figure class="highlight clean"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs clean"><span class="hljs-keyword">import</span> &#123;hello&#125; <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;my-library&#x27;</span>;<br>hello();<br></code></pre></td></tr></table></figure><p>使用<code>yarn start</code>并打开 JavaScript 控制台运行 React 应用程序，您应该会看到打印出“hello”字样。</p><p>现在要确保<code>tree-shaking</code>有效，请运行<code>yarn build</code>. React 应用程序将被打包并放入<code>build</code>目录中。如果你在文件中搜索<code>hello</code>关键字，你会看到它在一个很长的复杂名称的js文件中，但<code>goodbye</code>找不到关键字。这表明 webpack 只拉入了必要的代码。并且由于我们在我们的库中使用命名导出，我们库的使用者不能编写<code>import myLibrary from &#39;my-library&#39;;</code>和错误地导入整个包，而只使用其中的一小部分。</p><p>下面介绍<code>rollup</code>中的几种常用的插件以及<code>external</code>属性、<code>tree-shaking</code>机制。</p><h2 id="补充说明"><a href="#补充说明" class="headerlink" title="补充说明"></a>补充说明</h2><h3 id="resolve插件"><a href="#resolve插件" class="headerlink" title="resolve插件"></a><code>resolve</code>插件</h3><h4 id="为什么要使用resolve插件"><a href="#为什么要使用resolve插件" class="headerlink" title="为什么要使用resolve插件"></a>为什么要使用<code>resolve</code>插件</h4><p>在上面的入门案例中，我们打包的对象是本地的<code>js</code>代码和库，但实际开发中，不太可能所有的库都位于本地，我们大多会通过<code>npm</code>下载远程的库。</p><p>与<code>webpack</code>和<code>browserify</code>这样的其他打包器包不同，<code>rollup</code>不知道如何打破常规去处理这些依赖。因此我们需要添加一些配置。</p><h4 id="resolve插件使用"><a href="#resolve插件使用" class="headerlink" title="resolve插件使用"></a><code>resolve</code>插件使用</h4><p>首先在我们的项目中添加一个依赖<code>the-answer</code>，然后修改<code>src/index.js</code>文件:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> answer <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;the-answer&quot;</span>;<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;<br>  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;the answer is &quot;</span> + answer);<br>&#125;<br><br></code></pre></td></tr></table></figure><p>执行<code>npm run build</code>。</p><blockquote><p>这里为了方便，我将原本的<code>rollup -c -w</code>添加到了<code>package.json</code>的<code>scripts</code>中：<code>&quot;build&quot;: &quot;rollup -c -w&quot;</code></p></blockquote><p>会得到以下报错： <img src="https:////p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f5730d429a8a4d61ab28a839f3343394~tplv-k3u1fbpfcp-zoom-1.image" alt="img"> 打包后的<code>bundle.js</code>仍然会在<code>Node.js</code>中工作，但是<code>the-answer</code>不包含在包中。为了解决这个问题，将我们编写的源码与依赖的第三方库进行合并，<code>rollup.js</code>为我们提供了<code>resolve</code>插件。</p><p>首先，安装<code>resolve</code>插件：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">npm i -D @rollup/plugin-node-resolve<br></code></pre></td></tr></table></figure><p>修改配置文件<code>rollup.config.js</code>：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> resolve <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@rollup/plugin-node-resolve&quot;</span>;<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> &#123;<br>  <span class="hljs-attr">input</span>: [<span class="hljs-string">&quot;./src/index.js&quot;</span>],<br>  <span class="hljs-attr">output</span>: &#123;<br>    <span class="hljs-attr">file</span>: <span class="hljs-string">&quot;./dist/bundle.js&quot;</span>,<br>    <span class="hljs-attr">format</span>: <span class="hljs-string">&quot;umd&quot;</span>,<br>    <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;experience&quot;</span>,<br>  &#125;,<br>  <span class="hljs-attr">plugins</span>: [resolve()],<br>&#125;;<br><br></code></pre></td></tr></table></figure><p>这时再次执行<code>npm run build</code>，可以发现报错已经没有了： <img src="https:////p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4712a288683f44f2a871b2c518b02ab5~tplv-k3u1fbpfcp-zoom-1.image" alt="img"></p><p>打开<code>dist/bundle.js</code>文件：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs js">(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"><span class="hljs-built_in">global</span>, factory</span>) </span>&#123;<br>  <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">exports</span> === <span class="hljs-string">&#x27;object&#x27;</span> &amp;&amp; <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">module</span> !== <span class="hljs-string">&#x27;undefined&#x27;</span> ? <span class="hljs-built_in">module</span>.exports = factory() :<br>  <span class="hljs-keyword">typeof</span> define === <span class="hljs-string">&#x27;function&#x27;</span> &amp;&amp; define.amd ? define(factory) :<br>  (<span class="hljs-built_in">global</span> = <span class="hljs-keyword">typeof</span> globalThis !== <span class="hljs-string">&#x27;undefined&#x27;</span> ? globalThis : <span class="hljs-built_in">global</span> || self, <span class="hljs-built_in">global</span>.experience = factory());<br>&#125;(<span class="hljs-built_in">this</span>, (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123; <span class="hljs-string">&#x27;use strict&#x27;</span>;<br><br>  <span class="hljs-keyword">var</span> index = <span class="hljs-number">42</span>;<br><br>  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index$1</span> (<span class="hljs-params"></span>) </span>&#123;<br>    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;the answer is &quot;</span> + index);<br>  &#125;<br><br>  <span class="hljs-keyword">return</span> index$<span class="hljs-number">1</span>;<br><br>&#125;)));<br><br></code></pre></td></tr></table></figure><p>打包文件<code>bundle.js</code>中已经包含了引用的模块。</p><p>有些场景下，虽然我们使用了<code>resolve</code>插件，但可能我们仍然想要某些库保持外部引用状态，这时我们就需要使用<code>external</code>属性，来告诉<code>rollup.js</code>哪些是外部的类库。</p><h3 id="external-属性"><a href="#external-属性" class="headerlink" title="external 属性"></a>external 属性</h3><p>修改<code>rollup.js</code>的配置文件：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> resolve <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@rollup/plugin-node-resolve&quot;</span>;<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> &#123;<br>  <span class="hljs-attr">input</span>: [<span class="hljs-string">&quot;./src/index.js&quot;</span>],<br>  <span class="hljs-attr">output</span>: &#123;<br>    <span class="hljs-attr">file</span>: <span class="hljs-string">&quot;./dist/bundle.js&quot;</span>,<br>    <span class="hljs-attr">format</span>: <span class="hljs-string">&quot;umd&quot;</span>,<br>    <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;experience&quot;</span>,<br>  &#125;,<br>  <span class="hljs-attr">plugins</span>: [resolve()],<br>  <span class="hljs-attr">external</span>: [<span class="hljs-string">&quot;the-answer&quot;</span>],<br>&#125;;<br><br></code></pre></td></tr></table></figure><p>重新打包，打开<code>dist/bundle.js</code>文件：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs js">(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"><span class="hljs-built_in">global</span>, factory</span>) </span>&#123;<br>  <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">exports</span> === <span class="hljs-string">&#x27;object&#x27;</span> &amp;&amp; <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">module</span> !== <span class="hljs-string">&#x27;undefined&#x27;</span> ? <span class="hljs-built_in">module</span>.exports = factory(<span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;the-answer&#x27;</span>)) :<br>  <span class="hljs-keyword">typeof</span> define === <span class="hljs-string">&#x27;function&#x27;</span> &amp;&amp; define.amd ? define([<span class="hljs-string">&#x27;the-answer&#x27;</span>], factory) :<br>  (<span class="hljs-built_in">global</span> = <span class="hljs-keyword">typeof</span> globalThis !== <span class="hljs-string">&#x27;undefined&#x27;</span> ? globalThis : <span class="hljs-built_in">global</span> || self, <span class="hljs-built_in">global</span>.experience = factory(<span class="hljs-built_in">global</span>.answer));<br>&#125;(<span class="hljs-built_in">this</span>, (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">answer</span>) </span>&#123; <span class="hljs-string">&#x27;use strict&#x27;</span>;<br><br>  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_interopDefaultLegacy</span> (<span class="hljs-params">e</span>) </span>&#123; <span class="hljs-keyword">return</span> e &amp;&amp; <span class="hljs-keyword">typeof</span> e === <span class="hljs-string">&#x27;object&#x27;</span> &amp;&amp; <span class="hljs-string">&#x27;default&#x27;</span> <span class="hljs-keyword">in</span> e ? e : &#123; <span class="hljs-string">&#x27;default&#x27;</span>: e &#125;; &#125;<br><br>  <span class="hljs-keyword">var</span> answer__default = <span class="hljs-comment">/*#__PURE__*/</span>_interopDefaultLegacy(answer);<br><br>  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index</span> (<span class="hljs-params"></span>) </span>&#123;<br>    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;the answer is &quot;</span> + answer__default[<span class="hljs-string">&#x27;default&#x27;</span>]);<br>  &#125;<br><br>  <span class="hljs-keyword">return</span> index;<br><br>&#125;)));<br><br></code></pre></td></tr></table></figure><p>这时我们看到<code>the-answer</code>已经是做为外部库被引入了。</p><h3 id="commonjs插件"><a href="#commonjs插件" class="headerlink" title="commonjs插件"></a><code>commonjs</code>插件</h3><h4 id="为什么需要commonjs插件"><a href="#为什么需要commonjs插件" class="headerlink" title="为什么需要commonjs插件"></a>为什么需要<code>commonjs</code>插件</h4><p><code>rollup.js</code>编译源码中的模块引用默认只支持 <code>ES6+</code>的模块方式<code>import/export</code>。然而大量的<code>npm</code>模块是基于<code>CommonJS</code>模块方式，这就导致了大量 <code>npm</code>模块不能直接编译使用。</p><p>因此使得<code>rollup.js</code>编译支持<code>npm</code>模块和<code>CommonJS</code>模块方式的插件就应运而生：<code>@rollup/plugin-commonjs</code>。</p><h4 id="commonjs插件使用"><a href="#commonjs插件使用" class="headerlink" title="commonjs插件使用"></a><code>commonjs</code>插件使用</h4><p>首先，安装该模块：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">npm i -D @rollup/plugin-commonjs<br></code></pre></td></tr></table></figure><p>然后修改<code>rollup.config.js</code>文件：</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs pgsql"><span class="hljs-keyword">import</span> resolve <span class="hljs-keyword">from</span> &quot;@rollup/plugin-node-resolve&quot;;<br><span class="hljs-keyword">import</span> commonjs <span class="hljs-keyword">from</span> &quot;@rollup/plugin-commonjs&quot;;<br>export <span class="hljs-keyword">default</span> &#123;<br>  <span class="hljs-keyword">input</span>: [&quot;./src/index.js&quot;],<br>  output: &#123;<br>    file: &quot;./dist/bundle.js&quot;,<br>    <span class="hljs-keyword">format</span>: &quot;umd&quot;,<br>    <span class="hljs-type">name</span>: &quot;experience&quot;,<br>  &#125;,<br>  plugins: [resolve(), commonjs()],<br>  <span class="hljs-keyword">external</span>: [&quot;the-answer&quot;],<br>&#125;;<br><br></code></pre></td></tr></table></figure><h3 id="babel插件"><a href="#babel插件" class="headerlink" title="babel插件"></a><code>babel</code>插件</h3><h4 id="为什么需要babel插件？"><a href="#为什么需要babel插件？" class="headerlink" title="为什么需要babel插件？"></a>为什么需要<code>babel</code>插件？</h4><p>我们在<code>src</code>目录下添加<code>es6.js</code>文件(⚠️ 这里我们使用了 es6 中的箭头函数)：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> a = <span class="hljs-number">1</span>;<br><span class="hljs-keyword">const</span> b = <span class="hljs-number">2</span>;<br><span class="hljs-built_in">console</span>.log(a, b);<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> () =&gt; &#123;<br>  <span class="hljs-keyword">return</span> a + b;<br>&#125;;<br></code></pre></td></tr></table></figure><p>然后修改<code>rollup.config.js</code>配置文件：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> resolve <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@rollup/plugin-node-resolve&quot;</span>;<br><span class="hljs-keyword">import</span> commonjs <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@rollup/plugin-commonjs&quot;</span>;<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> &#123;<br>  <span class="hljs-attr">input</span>: [<span class="hljs-string">&quot;./src/es6.js&quot;</span>],<br>  <span class="hljs-attr">output</span>: &#123;<br>    <span class="hljs-attr">file</span>: <span class="hljs-string">&quot;./dist/esBundle.js&quot;</span>,<br>    <span class="hljs-attr">format</span>: <span class="hljs-string">&quot;umd&quot;</span>,<br>    <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;experience&quot;</span>,<br>  &#125;,<br>  <span class="hljs-attr">plugins</span>: [resolve(), commonjs()],<br>  <span class="hljs-attr">external</span>: [<span class="hljs-string">&quot;the-answer&quot;</span>],<br>&#125;;<br></code></pre></td></tr></table></figure><p>执行打包，可以看到<code>dist/esBundle.js</code>文件内容如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs js">(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"><span class="hljs-built_in">global</span>, factory</span>) </span>&#123;<br>  <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">exports</span> === <span class="hljs-string">&#x27;object&#x27;</span> &amp;&amp; <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">module</span> !== <span class="hljs-string">&#x27;undefined&#x27;</span> ? <span class="hljs-built_in">module</span>.exports = factory() :<br>  <span class="hljs-keyword">typeof</span> define === <span class="hljs-string">&#x27;function&#x27;</span> &amp;&amp; define.amd ? define(factory) :<br>  (<span class="hljs-built_in">global</span> = <span class="hljs-keyword">typeof</span> globalThis !== <span class="hljs-string">&#x27;undefined&#x27;</span> ? globalThis : <span class="hljs-built_in">global</span> || self, <span class="hljs-built_in">global</span>.experience = factory());<br>&#125;(<span class="hljs-built_in">this</span>, (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123; <span class="hljs-string">&#x27;use strict&#x27;</span>;<br><br>  <span class="hljs-keyword">const</span> a = <span class="hljs-number">1</span>;<br>  <span class="hljs-keyword">const</span> b = <span class="hljs-number">2</span>;<br>  <span class="hljs-built_in">console</span>.log(a, b);<br>  <span class="hljs-keyword">var</span> es6 = <span class="hljs-function">() =&gt;</span> &#123;<br>    <span class="hljs-keyword">return</span> a + b;<br>  &#125;;<br>  <span class="hljs-keyword">return</span> es6;<br>&#125;)));<br></code></pre></td></tr></table></figure><p>可以看到箭头函数被保留下来，这样的代码在不支持<code>ES6</code>的环境下将无法运行。我们期望在<code>rollup.js</code>打包的过程中就能使用<code>babel</code>完成代码转换，因此我们需要<code>babel</code>插件。</p><h4 id="babel插件的使用"><a href="#babel插件的使用" class="headerlink" title="babel插件的使用"></a><code>babel</code>插件的使用</h4><p>首先，安装：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">npm i -D @rollup/plugin-babel<br></code></pre></td></tr></table></figure><p>同样修改配置文件<code>rollup.config.js</code>：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> resolve <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@rollup/plugin-node-resolve&quot;</span>;<br><span class="hljs-keyword">import</span> commonjs <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@rollup/plugin-commonjs&quot;</span>;<br><span class="hljs-keyword">import</span> babel <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@rollup/plugin-babel&quot;</span>;<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> &#123;<br>  <span class="hljs-attr">input</span>: [<span class="hljs-string">&quot;./src/es6.js&quot;</span>],<br>  <span class="hljs-attr">output</span>: &#123;<br>    <span class="hljs-attr">file</span>: <span class="hljs-string">&quot;./dist/esBundle.js&quot;</span>,<br>    <span class="hljs-attr">format</span>: <span class="hljs-string">&quot;umd&quot;</span>,<br>    <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;experience&quot;</span>,<br>  &#125;,<br>  <span class="hljs-attr">plugins</span>: [resolve(), commonjs(), babel()],<br>  <span class="hljs-attr">external</span>: [<span class="hljs-string">&quot;the-answer&quot;</span>],<br>&#125;;<br></code></pre></td></tr></table></figure><p>然后打包，发现会出现报错： <img src="https:////p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c664d97500e84fba8a6c3e5e23b589fd~tplv-k3u1fbpfcp-zoom-1.image" alt="img"> 提示我们缺少<code>@babel/core</code>，因为<code>@babel/core</code>是<code>babel</code>的核心。我们来进行安装：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">npm i @babel/core<br></code></pre></td></tr></table></figure><p>再次执行打包，发现这次没有报错了，但是我们尝试打开<code>dist/esBundle.js</code>：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs js">(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"><span class="hljs-built_in">global</span>, factory</span>) </span>&#123;<br>  <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">exports</span> === <span class="hljs-string">&#x27;object&#x27;</span> &amp;&amp; <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">module</span> !== <span class="hljs-string">&#x27;undefined&#x27;</span> ? <span class="hljs-built_in">module</span>.exports = factory() :<br>  <span class="hljs-keyword">typeof</span> define === <span class="hljs-string">&#x27;function&#x27;</span> &amp;&amp; define.amd ? define(factory) :<br>  (<span class="hljs-built_in">global</span> = <span class="hljs-keyword">typeof</span> globalThis !== <span class="hljs-string">&#x27;undefined&#x27;</span> ? globalThis : <span class="hljs-built_in">global</span> || self, <span class="hljs-built_in">global</span>.experience = factory());<br>&#125;(<span class="hljs-built_in">this</span>, (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123; <span class="hljs-string">&#x27;use strict&#x27;</span>;<br><br>  <span class="hljs-keyword">const</span> a = <span class="hljs-number">1</span>;<br>  <span class="hljs-keyword">const</span> b = <span class="hljs-number">2</span>;<br>  <span class="hljs-built_in">console</span>.log(a, b);<br>  <span class="hljs-keyword">var</span> es6 = (<span class="hljs-function">() =&gt;</span> &#123;<br>    <span class="hljs-keyword">return</span> a + b;<br>  &#125;);<br>  <span class="hljs-keyword">return</span> es6;<br>&#125;)));<br></code></pre></td></tr></table></figure><p>可以发现箭头函数仍然存在，显然这是不正确的，说明我们的<code>babel</code>插件没有起到作用。这是为什么呢？</p><p>原因是由于我们缺少<code>.babelrc</code>文件，添加该文件：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs js">&#123;<br>  <span class="hljs-string">&quot;presets&quot;</span>: [<br>    [<br>      <span class="hljs-string">&quot;@babel/preset-env&quot;</span>,<br>      &#123;<br>        <span class="hljs-string">&quot;modules&quot;</span>: <span class="hljs-literal">false</span>,<br>        <span class="hljs-comment">// &quot;useBuiltIns&quot;: &quot;usage&quot;</span><br>      &#125;<br>    ]<br>  ]<br>&#125;<br></code></pre></td></tr></table></figure><p>我们看<code>.babelrc</code>配置了<code>preset env</code>，所以先安装这个插件：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">npm i @babel/preset-env<br></code></pre></td></tr></table></figure><p>这次再次执行打包，我们打开<code>dist/esBundle.js</code>文件：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs js">(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"><span class="hljs-built_in">global</span>, factory</span>) </span>&#123;<br>  <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">exports</span> === <span class="hljs-string">&#x27;object&#x27;</span> &amp;&amp; <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">module</span> !== <span class="hljs-string">&#x27;undefined&#x27;</span> ? <span class="hljs-built_in">module</span>.exports = factory() :<br>  <span class="hljs-keyword">typeof</span> define === <span class="hljs-string">&#x27;function&#x27;</span> &amp;&amp; define.amd ? define(factory) :<br>  (<span class="hljs-built_in">global</span> = <span class="hljs-keyword">typeof</span> globalThis !== <span class="hljs-string">&#x27;undefined&#x27;</span> ? globalThis : <span class="hljs-built_in">global</span> || self, <span class="hljs-built_in">global</span>.experience = factory());<br>&#125;(<span class="hljs-built_in">this</span>, (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123; <span class="hljs-string">&#x27;use strict&#x27;</span>;<br>  <span class="hljs-keyword">var</span> a = <span class="hljs-number">1</span>;<br>  <span class="hljs-keyword">var</span> b = <span class="hljs-number">2</span>;<br>  <span class="hljs-built_in">console</span>.log(a, b);<br>  <span class="hljs-keyword">var</span> es6 = (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;<br>    <span class="hljs-keyword">return</span> a + b;<br>  &#125;);<br>  <span class="hljs-keyword">return</span> es6;<br>&#125;)));<br></code></pre></td></tr></table></figure><p>可以看到箭头函数被转换为了<code>function</code>，说明<code>babel</code>插件正常工作。</p><h3 id="json插件"><a href="#json插件" class="headerlink" title="json插件"></a><code>json</code>插件</h3><h4 id="为什么要使用json插件？"><a href="#为什么要使用json插件？" class="headerlink" title="为什么要使用json插件？"></a>为什么要使用<code>json</code>插件？</h4><p>在<code>src</code>目录下创建<code>json.js</code>文件：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> json <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;../package.json&quot;</span>;<br><span class="hljs-built_in">console</span>.log(json.author);<br></code></pre></td></tr></table></figure><p>内容很简单，就是引入<code>package.json</code>，然后去打印<code>author</code>字段。</p><p>修改<code>rollup.config.js</code>配置文件：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> resolve <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@rollup/plugin-node-resolve&quot;</span>;<br><span class="hljs-keyword">import</span> commonjs <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@rollup/plugin-commonjs&quot;</span>;<br><span class="hljs-keyword">import</span> babel <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@rollup/plugin-babel&quot;</span>;<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> &#123;<br>  <span class="hljs-attr">input</span>: [<span class="hljs-string">&quot;./src/json.js&quot;</span>],<br>  <span class="hljs-attr">output</span>: &#123;<br>    <span class="hljs-attr">file</span>: <span class="hljs-string">&quot;./dist/jsonBundle.js&quot;</span>,<br>    <span class="hljs-attr">format</span>: <span class="hljs-string">&quot;umd&quot;</span>,<br>    <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;experience&quot;</span>,<br>  &#125;,<br>  <span class="hljs-attr">plugins</span>: [resolve(), commonjs(), babel()],<br>  <span class="hljs-attr">external</span>: [<span class="hljs-string">&quot;the-answer&quot;</span>],<br>&#125;;<br></code></pre></td></tr></table></figure><p>执行打包，发现会发生如下报错： </p><p><img src="https:////p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/20c6782fd3d34c3b93c1178fbfa69338~tplv-k3u1fbpfcp-zoom-1.image" alt="img"> 提示我们缺少<code>@rollup/plugin-json</code>插件来支持<code>json</code>文件。</p><h4 id="json插件的使用"><a href="#json插件的使用" class="headerlink" title="json插件的使用"></a><code>json</code>插件的使用</h4><p>来安装该插件：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">npm i -D @rollup/plugin-json<br></code></pre></td></tr></table></figure><p>同样修改下配置文件，将插件加入<code>plugins</code>数组即可。</p><p>然后再次打包，发现打包成功了，我们打开生成的<code>dist/jsonBundle</code>目录：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><code class="hljs js">(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">factory</span>) </span>&#123;<br>  <span class="hljs-keyword">typeof</span> define === <span class="hljs-string">&#x27;function&#x27;</span> &amp;&amp; define.amd ? define(factory) :<br>  factory();<br>&#125;((<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123; <span class="hljs-string">&#x27;use strict&#x27;</span>;<br><br>  <span class="hljs-keyword">var</span> name = <span class="hljs-string">&quot;rollup-experience&quot;</span>;<br>  <span class="hljs-keyword">var</span> version = <span class="hljs-string">&quot;1.0.0&quot;</span>;<br>  <span class="hljs-keyword">var</span> description = <span class="hljs-string">&quot;&quot;</span>;<br>  <span class="hljs-keyword">var</span> main = <span class="hljs-string">&quot;index.js&quot;</span>;<br>  <span class="hljs-keyword">var</span> directories = &#123;<br>  <span class="hljs-attr">example</span>: <span class="hljs-string">&quot;example&quot;</span><br>  &#125;;<br>  <span class="hljs-keyword">var</span> scripts = &#123;<br>  <span class="hljs-attr">build</span>: <span class="hljs-string">&quot;rollup -c -w&quot;</span>,<br>  <span class="hljs-attr">test</span>: <span class="hljs-string">&quot;echo \&quot;Error: no test specified\&quot; &amp;&amp; exit 1&quot;</span><br>  &#125;;<br>  <span class="hljs-keyword">var</span> author = <span class="hljs-string">&quot;Cosen&quot;</span>;<br>  <span class="hljs-keyword">var</span> license = <span class="hljs-string">&quot;ISC&quot;</span>;<br>  <span class="hljs-keyword">var</span> dependencies = &#123;<br>  <span class="hljs-string">&quot;@babel/core&quot;</span>: <span class="hljs-string">&quot;^7.11.6&quot;</span>,<br>  <span class="hljs-string">&quot;@babel/preset-env&quot;</span>: <span class="hljs-string">&quot;^7.11.5&quot;</span>,<br>  <span class="hljs-string">&quot;the-answer&quot;</span>: <span class="hljs-string">&quot;^1.0.0&quot;</span><br>  &#125;;<br>  <span class="hljs-keyword">var</span> devDependencies = &#123;<br>  <span class="hljs-string">&quot;@rollup/plugin-babel&quot;</span>: <span class="hljs-string">&quot;^5.2.0&quot;</span>,<br>  <span class="hljs-string">&quot;@rollup/plugin-commonjs&quot;</span>: <span class="hljs-string">&quot;^15.0.0&quot;</span>,<br>  <span class="hljs-string">&quot;@rollup/plugin-json&quot;</span>: <span class="hljs-string">&quot;^4.1.0&quot;</span>,<br>  <span class="hljs-string">&quot;@rollup/plugin-node-resolve&quot;</span>: <span class="hljs-string">&quot;^9.0.0&quot;</span><br>  &#125;;<br>  <span class="hljs-keyword">var</span> json = &#123;<br>  <span class="hljs-attr">name</span>: name,<br>  <span class="hljs-attr">version</span>: version,<br>  <span class="hljs-attr">description</span>: description,<br>  <span class="hljs-attr">main</span>: main,<br>  <span class="hljs-attr">directories</span>: directories,<br>  <span class="hljs-attr">scripts</span>: scripts,<br>  <span class="hljs-attr">author</span>: author,<br>  <span class="hljs-attr">license</span>: license,<br>  <span class="hljs-attr">dependencies</span>: dependencies,<br>  <span class="hljs-attr">devDependencies</span>: devDependencies<br>  &#125;;<br>  <span class="hljs-built_in">console</span>.log(json.author);<br>&#125;)));<br></code></pre></td></tr></table></figure><p>完美！！</p><h3 id="tree-shaking机制"><a href="#tree-shaking机制" class="headerlink" title="tree-shaking机制"></a><code>tree-shaking</code>机制</h3><p>这里我们以最开始的<code>src/index.js</code>为例进行说明：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> answer <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;the-answer&quot;</span>;<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;<br>  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;the answer is &quot;</span> + answer);<br>&#125;<br></code></pre></td></tr></table></figure><p>修改上述文件：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> a = <span class="hljs-number">1</span>;<br><span class="hljs-keyword">const</span> b = <span class="hljs-number">2</span>;<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;<br>  <span class="hljs-built_in">console</span>.log(a + b);<br>&#125;<br></code></pre></td></tr></table></figure><p>执行打包。打开<code>dist/bundle.js</code>文件：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs js">(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"><span class="hljs-built_in">global</span>, factory</span>) </span>&#123;<br>  <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">exports</span> === <span class="hljs-string">&#x27;object&#x27;</span> &amp;&amp; <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">module</span> !== <span class="hljs-string">&#x27;undefined&#x27;</span> ? <span class="hljs-built_in">module</span>.exports = factory() :<br>  <span class="hljs-keyword">typeof</span> define === <span class="hljs-string">&#x27;function&#x27;</span> &amp;&amp; define.amd ? define(factory) :<br>  (<span class="hljs-built_in">global</span> = <span class="hljs-keyword">typeof</span> globalThis !== <span class="hljs-string">&#x27;undefined&#x27;</span> ? globalThis : <span class="hljs-built_in">global</span> || self, <span class="hljs-built_in">global</span>.experience = factory());<br>&#125;(<span class="hljs-built_in">this</span>, (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123; <span class="hljs-string">&#x27;use strict&#x27;</span>;<br><br>  <span class="hljs-keyword">var</span> a = <span class="hljs-number">1</span>;<br>  <span class="hljs-keyword">var</span> b = <span class="hljs-number">2</span>;<br>  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index</span> (<span class="hljs-params"></span>) </span>&#123;<br>    <span class="hljs-built_in">console</span>.log(a + b);<br>  &#125;<br>  <span class="hljs-keyword">return</span> index;<br>&#125;)));<br></code></pre></td></tr></table></figure><p>再次修改<code>src/index.js</code>文件：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> a = <span class="hljs-number">1</span>;<br><span class="hljs-keyword">const</span> b = <span class="hljs-number">2</span>;<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;<br>  <span class="hljs-built_in">console</span>.log(a);<br>&#125;<br></code></pre></td></tr></table></figure><p>再次执行打包，打开打包文件：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs js">(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"><span class="hljs-built_in">global</span>, factory</span>) </span>&#123;<br>  <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">exports</span> === <span class="hljs-string">&#x27;object&#x27;</span> &amp;&amp; <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">module</span> !== <span class="hljs-string">&#x27;undefined&#x27;</span> ? <span class="hljs-built_in">module</span>.exports = factory() :<br>  <span class="hljs-keyword">typeof</span> define === <span class="hljs-string">&#x27;function&#x27;</span> &amp;&amp; define.amd ? define(factory) :<br>  (<span class="hljs-built_in">global</span> = <span class="hljs-keyword">typeof</span> globalThis !== <span class="hljs-string">&#x27;undefined&#x27;</span> ? globalThis : <span class="hljs-built_in">global</span> || self, <span class="hljs-built_in">global</span>.experience = factory());<br>&#125;(<span class="hljs-built_in">this</span>, (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123; <span class="hljs-string">&#x27;use strict&#x27;</span>;<br><br>  <span class="hljs-keyword">var</span> a = <span class="hljs-number">1</span>;<br>  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">index</span> (<span class="hljs-params"></span>) </span>&#123;<br>    <span class="hljs-built_in">console</span>.log(a);<br>  &#125;<br>  <span class="hljs-keyword">return</span> index;<br>&#125;)));<br></code></pre></td></tr></table></figure><p>发现了什么？</p><p>我们发现关于变量<code>b</code>的定义没有了，因为源码中并没有用到这个变量。这就是<code>ES</code>模块著名的<code>tree-shaking</code>机制，它动态地清除没有被使用过的代码，使得代码更加精简，从而可以使得我们的类库获得更快的加载速度。</p><p>希望这会有所帮助，如果您有任何疑问，请在评论中告诉我！</p><blockquote><p>  <a href="https://www.jianshu.com/p/6a7413481bd2">https://www.jianshu.com/p/6a7413481bd2</a></p><p>  <a href="https://juejin.cn/post/6869551115420041229#heading-1">https://juejin.cn/post/6869551115420041229#heading-1</a></p></blockquote>]]></content>
    
    
    
    <tags>
      
      <tag>Rollup</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>安装node-sass的几个方法</title>
    <link href="/blog/posts/b6b80389.html"/>
    <url>/blog/posts/b6b80389.html</url>
    
    <content type="html"><![CDATA[<p>安装 <a href="https://github.com/sass/node-sass">node-sass</a> 的时候总是会各种不成功，安装 <code>node-sass</code> 时在 <code>node scripts/install</code> 阶段会从 github.com 上下载一个 <code>.node</code> 文件，大部分安装不成功的原因都源自这里，因为 GitHub Releases 里的文件都托管在 <code>s3.amazonaws.com</code> 上面，而这个网址在国内总是<em>网络不稳定</em>，所以我们需要通过第三方服务器下载这个文件。<a href="https://github.com/lmk123/blog/issues/28">https://github.com/lmk123/blog/issues/28</a></p><h2 id="方法一：使用淘宝镜像"><a href="#方法一：使用淘宝镜像" class="headerlink" title="方法一：使用淘宝镜像"></a>方法一：使用淘宝镜像</h2><p>macOS 系统直接运行下面的命令即可：</p><figure class="highlight crmsh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs crmsh"><span class="hljs-attr">SASS_BINARY_SITE=</span>https://npm.taobao.org/mirrors/<span class="hljs-keyword">node</span><span class="hljs-title">-sass</span>/ npm install <span class="hljs-keyword">node</span><span class="hljs-title">-sass</span><br></code></pre></td></tr></table></figure><p>我们一般更希望能跨平台、并且直接使用 <code>npm install</code> 安装所有依赖，所以我的做法是在项目内添加一个 <code>.npmrc</code> 文件：</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs awk">sass_binary_site=https:<span class="hljs-regexp">//</span>npm.taobao.org<span class="hljs-regexp">/mirrors/</span>node-sass/<br>phantomjs_cdnurl=https:<span class="hljs-regexp">//</span>npm.taobao.org<span class="hljs-regexp">/mirrors/</span>phantomjs/<br>electron_mirror=https:<span class="hljs-regexp">//</span>npm.taobao.org<span class="hljs-regexp">/mirrors/</span>electron/<br>registry=https:<span class="hljs-regexp">//</span>registry.npm.taobao.org<br></code></pre></td></tr></table></figure><p>这样使用 <code>npm install</code> 安装 <code>node-sass</code>、<code>electron</code> 和 <code>phantomjs</code> 时都能自动从淘宝源上下载，但是在使用 <code>npm publish</code> 的时候要把 <code>registry</code> 这一行给注释掉，否则就会发布到淘宝源上去了。</p><h2 id="方法二：使用梯子"><a href="#方法二：使用梯子" class="headerlink" title="方法二：使用梯子"></a>方法二：使用梯子</h2><p>假设你的梯子在你本地机器上开启了一个第三方服务器 <code>127.0.0.1:1080</code>，那么只需按照下面的方法配置一下就能正常安装 <code>node-sass</code> 了（如果你开启的是 PAC 模式而不是全局模式，那还需要将 <code>s3.amazonaws.com</code> 加入 PAC 列表）：</p><figure class="highlight gams"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs gams">npm config <span class="hljs-keyword">set</span> proxy <span class="hljs-comment">http:</span>//<span class="hljs-comment">127.0.0.1:1080</span><br>npm <span class="hljs-comment">i node-sass</span><br><br># 下载完成后删除 http 代理<br>npm <span class="hljs-comment">config delete proxy</span><br></code></pre></td></tr></table></figure><p>嗯，这样下来就能正常安装了。</p><h2 id="方法三：可以考虑使用-dart-sass-替换-node-sass。"><a href="#方法三：可以考虑使用-dart-sass-替换-node-sass。" class="headerlink" title="方法三：可以考虑使用 dart-sass 替换 node-sass。"></a>方法三：可以考虑使用 dart-sass 替换 node-sass。</h2><p>node-sass 跟 node-gyp 两个库都很难装，可以直接这样写用 dart-sass 替换。</p><img src="/blog/posts/b6b80389/64eiAdFYYOEYBl3TK9Il_04_84fc625b592800cb072e98f75071f08c_image.png" class="" title="img">]]></content>
    
    
    
    <tags>
      
      <tag>Node-sass</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>supervisor的使用记录</title>
    <link href="/blog/posts/8479a1a4.html"/>
    <url>/blog/posts/8479a1a4.html</url>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>为啥使用supervisor，因为最近给我的<a href="https://api.gishai.top/">API服务</a>增加了一个定时任务，需要后台一直运行一条命令行，当关掉这个命令行时任务就不会跑了，各种搜索，定位到supervisor能解决这个问题。于是了解了一下，发现Supervisor还能解决Django项目不能自动启动的问题。Supervisor是用Python开发的一套通用的进程管理程序，能将一个普通的命令行进程变为后台daemon，并监控进程状态，异常退出时能自动重启。它是通过fork/exec的方式把这些被管理的进程当作supervisor的子进程来启动，这样只要在supervisor的配置文件中，把要管理的进程的可执行文件的路径写进去即可。也实现当子进程挂掉的时候，父进程可以准确获取子进程挂掉的信息的，可以选择是否自己启动和报警。supervisor还提供了一个功能，可以为supervisord或者每个子进程，设置一个非root的user，这个user就可以管理它对应的进程。</p><h2 id="supervisor安装"><a href="#supervisor安装" class="headerlink" title="supervisor安装"></a>supervisor安装</h2><p>centos7 系统yum安装的supervisor是3.<em>.</em>版本，需要使用python的pip安装最新版本(4.2.3)<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">pip3 install supervisor<br></code></pre></td></tr></table></figure><br>由于本机python是编译安装，所以supervisor安装后所在目录为:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">/usr/<span class="hljs-built_in">local</span>/python3/bin/supervisorctl  (客户端（用于和守护进程通信，发送管理进程的指令）)<br>/usr/<span class="hljs-built_in">local</span>/python3/bin/supervisord  (是supervisor的守护进程服务（用于接收进程管理命令))<br>/usr/<span class="hljs-built_in">local</span>/python3/bin/echo_supervisord_conf (生成初始配置文件程序)<br></code></pre></td></tr></table></figure><br>然后要将这个路径添加到环境变量中<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">将 PATH=<span class="hljs-variable">$PATH</span>:<span class="hljs-variable">$HOME</span>/bin:/usr/<span class="hljs-built_in">local</span>/python3/bin<br>添加到用户目录文件 .bash_profile 里面, 其他能够执行环境变量初始化的文件也可以。<br></code></pre></td></tr></table></figure></p><h2 id="配置supervisor"><a href="#配置supervisor" class="headerlink" title="配置supervisor"></a>配置supervisor</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">mkdir -p /etc/supervisor/conf.d/<br>echo_supervisord_conf &gt; /etc/supervisor/supervisord.conf<br></code></pre></td></tr></table></figure><p>在/etc/supervisor/supervisord.conf 的[include]下添加:<br><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs shell">;[include]<br>;files = relative/directory/*.ini<br><span class="hljs-meta">#</span><span class="bash"> 修改为:</span><br>[include]<br>files = /etc/supervisor/conf.d/*.conf<br><span class="hljs-meta">#</span><span class="bash"> (注意去掉分号,第一次安装的时候就因为没去掉分号出现了问题!);</span><br></code></pre></td></tr></table></figure></p><h2 id="配置开机自启动"><a href="#配置开机自启动" class="headerlink" title="配置开机自启动"></a>配置开机自启动</h2><h3 id="官方脚本"><a href="#官方脚本" class="headerlink" title="官方脚本"></a>官方脚本</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">wget -O /usr/lib/systemd/system/supervisord.service  https://github.com/Supervisor/initscripts/raw/master/centos-systemd-etcs<br></code></pre></td></tr></table></figure><h3 id="手动编写"><a href="#手动编写" class="headerlink" title="手动编写"></a>手动编写</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># supervisord service for systemd (CentOS 7.0+)</span><br>[Unit]<br>Description=Supervisor daemon<br>After=network.target<br><br>[Service]<br>Type=forking<br><span class="hljs-comment"># 第一个参数必须是可执行文件的绝对路径，不接受替代</span><br>ExecStart=/usr/<span class="hljs-built_in">local</span>/python3/bin/supervisord -c /etc/supervisor/supervisord.conf<br>ExecStop=/usr/<span class="hljs-built_in">local</span>/python3/bin/supervisorctl <span class="hljs-variable">$OPTIONS</span> shutdown<br>ExecReload=/usr/<span class="hljs-built_in">local</span>/python3/bin/supervisorctl <span class="hljs-variable">$OPTIONS</span> reload<br>KillMode=process<br>Restart=on-failure<br>RestartSec=42s<br><br>[Install]<br>WantedBy=multi-user.target<br></code></pre></td></tr></table></figure><h3 id="设置开机启动"><a href="#设置开机启动" class="headerlink" title="设置开机启动"></a>设置开机启动</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash">systemctl <span class="hljs-built_in">enable</span> supervisord.service<br>systemctl daemon-reload <br>systemctl list-unit-files|grep enabled <span class="hljs-comment">#查看是否开启成功</span><br><br>chmod 766 supervisord.service <span class="hljs-comment"># 修改文件权限</span><br></code></pre></td></tr></table></figure><h3 id="编辑监控程序文件"><a href="#编辑监控程序文件" class="headerlink" title="编辑监控程序文件"></a>编辑监控程序文件</h3><p>在/etc/supervisor/conf.d 下新建程序文件，比如cmd.conf<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs bash">[program:somecmd]<br><span class="hljs-built_in">command</span>=/usr/<span class="hljs-built_in">local</span>/python3/bin/python cmd.py<br>numprocs=1<br>autostart=<span class="hljs-literal">true</span><br>autorestart=<span class="hljs-literal">true</span><br>startretries=3<br>user=nfuser<br>redirect_stderr=<span class="hljs-literal">true</span><br>stdout_logfile=/var/<span class="hljs-built_in">log</span>/cmd-stdout.log<br>stdout_logfile_maxbytes=10MB<br>stdout_logfile_backups=20<br></code></pre></td></tr></table></figure><br>如果有多个程序，可以配置为组:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs bash">; The below sample group section shows all possible group values,<br>; create one or more <span class="hljs-string">&#x27;real&#x27;</span> group: sections to create <span class="hljs-string">&quot;heterogeneous&quot;</span><br>; process groups.<br><br>;[group:thegroupname]<br>;programs=php-fpm,nginx,redis,mysql  ; 定义该组有哪些程序，程序之间用逗号分隔，注意，这些程序必须在前面用“[program:theprogramname]”模块定义过。<br>;priority=999                        ; 启动优先级，假设有多个组，每个组一个优先级，越小越优先执行 (默认 999)<br></code></pre></td></tr></table></figure></p><h2 id="启动supervisor"><a href="#启动supervisor" class="headerlink" title="启动supervisor"></a>启动supervisor</h2><h3 id="手动启动supervisor"><a href="#手动启动supervisor" class="headerlink" title="手动启动supervisor"></a>手动启动supervisor</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs shell">supervisord -c /etc/supervisor/supervisord.conf<br>ps -ef | grep supervisord # 查看服务是否已启动<br>cat /var/log/cmd-stdout.log # 通过日志查看<br></code></pre></td></tr></table></figure><h3 id="通过systemd命令启动"><a href="#通过systemd命令启动" class="headerlink" title="通过systemd命令启动"></a>通过systemd命令启动</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs shell">systemctl status supervisord # 查看supervisord状态<br>systemctl start supervisord<br>systemctl restart supervisord<br>systemctl stop supervisord<br></code></pre></td></tr></table></figure><h3 id="修改cmd配置文件后重新载入"><a href="#修改cmd配置文件后重新载入" class="headerlink" title="修改cmd配置文件后重新载入"></a>修改cmd配置文件后重新载入</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash">supervisorctl reread<br>supervisorctl update<br>supervisorctl restart somecmd<br><br></code></pre></td></tr></table></figure><h3 id="修改-etc-supervisor-supervisord-conf后重新载入"><a href="#修改-etc-supervisor-supervisord-conf后重新载入" class="headerlink" title="修改/etc/supervisor/supervisord.conf后重新载入"></a>修改/etc/supervisor/supervisord.conf后重新载入</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">supervisorctl reload<br></code></pre></td></tr></table></figure><h3 id="一些其他可能用到的命令"><a href="#一些其他可能用到的命令" class="headerlink" title="一些其他可能用到的命令"></a>一些其他可能用到的命令</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs shell">supervisorctl start programname      启动某个进程  <br>supervisorctl stop programname       停止某个进程<br>supervisorctl restart programname    重启某个进程<br>supervisorctl start all     启动全部进程<br>supervisorctl stop all      停止全部进程，注：start、restart、stop都不会载入最新的配置文件。<br>supervisorctl status        查看状态<br>supervisorctl shutdown      关闭supervisor<br></code></pre></td></tr></table></figure><h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><h3 id="Django-uWsgi-supervisor"><a href="#Django-uWsgi-supervisor" class="headerlink" title="Django+uWsgi+supervisor"></a>Django+uWsgi+supervisor</h3><ul><li>uWsgi配置</li></ul><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs sh">[uwsgi]<br><span class="hljs-comment"># Django-related settings</span><br><span class="hljs-comment">#uwsgi 备注</span><br><span class="hljs-comment"># start ===&gt; uwsgi --ini /hainergy/script/uwsgi.ini</span><br><span class="hljs-comment"># reload ===&gt; uwsgi --reload /hainergy/pidfile/uwsgi.pid</span><br><span class="hljs-comment"># stop ===&gt; uwsgi --stop /hainergy/pidfile/uwsgi.pid</span><br><br><span class="hljs-comment"># the base directory (full path)</span><br>home = /root/.virtualenvs/py38<br><span class="hljs-built_in">chdir</span> = /home/web/dogdog<br>module = dogdog.wsgi:application<br><span class="hljs-comment"># master</span><br>master = True<br>pidfile=/hainergy/pidfile/uwsgi.pid<br><span class="hljs-comment"># daemonize = /hainergy/log/uwsgi.log</span><br><span class="hljs-comment"># logto = /hainergy/log/uwsgi.log</span><br><span class="hljs-comment"># maximum number of worker processes</span><br>processes = 10<br><span class="hljs-comment"># the socket (use the full path to be safe)</span><br>socket = 127.0.0.1:9999         <span class="hljs-comment"># 云服务器内部ip</span><br>uid = root<br>gid = root<br>workers = 1<br>reload-mercy = 10<br><span class="hljs-comment"># clear environment on exit</span><br>vacuum = True<br>max-requests = 1000<br><span class="hljs-comment"># max virtual</span><br>limit-as = 1024   <br>reload-on-as =1024<br>buffer-size = 30000<br><span class="hljs-comment">#py-autoreload=1</span><br></code></pre></td></tr></table></figure><ul><li>nginx</li></ul><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs ini">location /static&#123;<br>  alias /home/web/doggo/static<span class="hljs-comment">;</span><br>&#125;<br><br>location /media &#123;<br>  alias /home/web/doggo/media<span class="hljs-comment">;</span><br>&#125;<br><br>location / &#123;<br>  include /usr/local/nginx/conf/uwsgi_params<span class="hljs-comment">;</span><br>  uwsgi_pass 127.0.0.1:9999<span class="hljs-comment">;</span><br>  <span class="hljs-comment"># index  templates/talk.html talk.htm;</span><br>&#125;<br></code></pre></td></tr></table></figure><ul><li>supervisor</li></ul><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-section">[program:djangogo]</span><br><span class="hljs-attr">command</span>=/usr/local/python3/bin/uwsgi --ini /hainergy/script/uwsgi.ini<br><span class="hljs-attr">user</span>=root<br><span class="hljs-attr">autorestart</span>=<span class="hljs-literal">true</span><br><span class="hljs-attr">autostart</span>=<span class="hljs-literal">true</span><br><span class="hljs-attr">startretries</span>=<span class="hljs-number">3</span><br><span class="hljs-attr">redirect_stderr</span>=<span class="hljs-literal">true</span><br><span class="hljs-attr">startsecs</span>=<span class="hljs-number">5</span><br><span class="hljs-attr">stdout_logfile</span>=/hainergy/log/aigisss.log<br><span class="hljs-attr">stopasgroup</span>=<span class="hljs-literal">true</span><br><span class="hljs-attr">killasgroup</span>=<span class="hljs-literal">true</span><br><span class="hljs-attr">priority</span>=<span class="hljs-number">999</span><br></code></pre></td></tr></table></figure><h3 id="Django-django-q"><a href="#Django-django-q" class="headerlink" title="Django+django_q"></a>Django+django_q</h3><ul><li>supervisor </li></ul><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-section">[program:qcluster]</span><br><span class="hljs-attr">directory</span>=/home/web/doggo<br><span class="hljs-attr">command</span>=/root/.virtualenvs/aigisss_py/bin/python manage.py qcluster<br><span class="hljs-attr">stopasgroup</span> = <span class="hljs-literal">true</span><br><span class="hljs-attr">user</span>=root<br><span class="hljs-attr">stdout_logfile</span>=/hainergy/log/qcluster.log<br></code></pre></td></tr></table></figure><h3 id="node-supercisor"><a href="#node-supercisor" class="headerlink" title="node+supercisor"></a>node+supercisor</h3><ul><li>supervisor</li></ul><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-section">[program:tilemap]</span><br><span class="hljs-attr">directory</span>=/home/server/mbserver<br><span class="hljs-attr">command</span>=/usr/local/nodejs/bin/pm2 start app.js<br><span class="hljs-attr">user</span>=root<br><span class="hljs-attr">autorestart</span>=<span class="hljs-literal">true</span><br><span class="hljs-attr">autostart</span>=<span class="hljs-literal">true</span><br><span class="hljs-attr">startretries</span>=<span class="hljs-number">3</span><br><span class="hljs-attr">redirect_stderr</span>=<span class="hljs-literal">true</span><br><span class="hljs-attr">startsecs</span>=<span class="hljs-number">5</span><br><span class="hljs-attr">stdout_logfile</span>=/hainergy/log/tilemap.log<br><span class="hljs-attr">stopasgroup</span>=<span class="hljs-literal">true</span><br><span class="hljs-attr">killasgroup</span>=<span class="hljs-literal">true</span><br><span class="hljs-attr">priority</span>=<span class="hljs-number">999</span><br></code></pre></td></tr></table></figure>]]></content>
    
    
    
    <tags>
      
      <tag>Supervisor</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>基于hexo的fluid主题的魔改汇总</title>
    <link href="/blog/posts/1563abd8.html"/>
    <url>/blog/posts/1563abd8.html</url>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p><strong>所谓魔改，就是与主题不同，一旦主题已经被采用或者被实现，<del>则会被划掉</del>，而且魔改初心是使用无侵入式的方式修改——即不修改源码，而是使用注入器的方式。这样的话主题升级比较容易！</strong></p><h2 id="背景固定"><a href="#背景固定" class="headerlink" title="背景固定"></a>背景固定</h2><p>效果如<a href="https://gishai.top/blog/posts/1563abd8.html">背景所示</a>，这种效果贯穿着整个博客。具体做法如下：使用<code>注入器</code>(如果没有<code>injector.js</code>文件，则在<code>scripts</code>文件夹下新建<code>injector.js</code>)，在<code>injector.js</code>中写下这些代码。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// 全屏背景的需要导入这些js</span><br><span class="hljs-keyword">const</span> &#123; <span class="hljs-attr">root</span>: siteRoot = <span class="hljs-string">&quot;/&quot;</span> &#125; = hexo.config;<br>hexo.extend.injector.register(<span class="hljs-string">&quot;body_begin&quot;</span>, <span class="hljs-string">`&lt;div id=&quot;web_bg&quot;&gt;&lt;/div&gt;`</span>);<br>hexo.extend.injector.register(<br>  <span class="hljs-string">&quot;body_end&quot;</span>,<br>  <span class="hljs-string">`&lt;script src=&quot;<span class="hljs-subst">$&#123;siteRoot&#125;</span>js/backgroundize.js&quot;&gt;&lt;/script&gt;</span><br><span class="hljs-string">  &lt;link defer rel=&quot;stylesheet&quot; href=&quot;<span class="hljs-subst">$&#123;siteRoot&#125;</span>styles/backgroundize.css&quot; /&gt;</span><br><span class="hljs-string">  `</span><br>);<br></code></pre></td></tr></table></figure><p>在<code>js</code>文件夹中新建<code>backgroundize.js</code>文件，内容如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> bannerContainer = $(<span class="hljs-string">&quot;#banner&quot;</span>);<br><span class="hljs-keyword">const</span> viewBg = $(<span class="hljs-string">&quot;#web_bg&quot;</span>);<br><span class="hljs-keyword">const</span> bannerMask = $(<span class="hljs-string">&quot;#banner .mask&quot;</span>);<br><span class="hljs-keyword">const</span> bg = $(bannerContainer).css(<span class="hljs-string">&quot;background-image&quot;</span>);<br>$(viewBg).css(<span class="hljs-string">&quot;background-image&quot;</span>, bg);<br>$(bannerContainer).css(<span class="hljs-string">&quot;background-image&quot;</span>, <span class="hljs-string">&quot;url()&quot;</span>);<br><span class="hljs-keyword">const</span> color = $(bannerMask).css(<span class="hljs-string">&quot;background-color&quot;</span>);<br>$(bannerMask).css(<span class="hljs-string">&quot;background-color&quot;</span>, <span class="hljs-string">`rgba(0,0,0,0)`</span>);<br>$(viewBg).css(<span class="hljs-string">&quot;background-color&quot;</span>, color);<br></code></pre></td></tr></table></figure><p>在<code>styles</code>文件夹中新增<code>backgroundize.css</code>文件，内容如下：</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs css"><span class="hljs-selector-id">#web_bg</span> &#123;<br>  <span class="hljs-attribute">position</span>: fixed;<br>  <span class="hljs-attribute">z-index</span>: -<span class="hljs-number">999</span>;<br>  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;<br>  <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;<br>  <span class="hljs-attribute">background-attachment</span>: local;<br>  <span class="hljs-attribute">background-position</span>: center;<br>  -webkit-<span class="hljs-attribute">background-size</span>: cover;<br>  -moz-<span class="hljs-attribute">background-size</span>: cover;<br>  <span class="hljs-attribute">background-size</span>: cover;<br>  <span class="hljs-attribute">background-repeat</span>: repeat;<br>&#125;<br></code></pre></td></tr></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>如有疑问、建议或者想知道本博客魔改的地方，欢迎在下面留言！</p>]]></content>
    
    
    
    <tags>
      
      <tag>Hexo</tag>
      
      <tag>Fluid</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>在任意hexo主题博客中添加github日历</title>
    <link href="/blog/posts/91dc324b.html"/>
    <url>/blog/posts/91dc324b.html</url>
    
    <content type="html"><![CDATA[<h2 id="前言：在博客中添加github的commit的日历，在我博客中的使用的效果如下图："><a href="#前言：在博客中添加github的commit的日历，在我博客中的使用的效果如下图：" class="headerlink" title="前言：在博客中添加github的commit的日历，在我博客中的使用的效果如下图："></a>前言：在博客中添加github的commit的日历，在我博客中的使用的效果如下图：</h2><img src="/blog/posts/91dc324b/image-20210418160637890.png" class="" title="image-20210418160637890"><p>与其说在任意hexo主题博客中添加github日历，倒不如说是<code>hexo-githubcalendar</code>的使用。</p><h2 id="安装hexo-githubcalendar"><a href="#安装hexo-githubcalendar" class="headerlink" title="安装hexo-githubcalendar"></a>安装hexo-githubcalendar</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs shell">npm i hexo-githubcalendar --save<br><span class="hljs-meta">#</span><span class="bash"> 或者</span><br>cnpm i hexo-githubcalendar --save<br></code></pre></td></tr></table></figure><p>注意，一定要加 <code>--save</code>，不然本地预览的时候可能不会显示！！！</p><h2 id="新增根目录-config-配置项-不是主题的-："><a href="#新增根目录-config-配置项-不是主题的-：" class="headerlink" title="新增根目录_config 配置项 (不是主题的)："></a>新增根目录_config 配置项 (不是主题的)：</h2><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># Ice Kano Plus_in</span><br><span class="hljs-comment"># Hexo Github Canlendar</span><br><span class="hljs-comment"># Author: Ice Kano</span><br><span class="hljs-comment"># Modify: Lete乐特</span><br><span class="hljs-attr">githubcalendar:</span><br>  <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span>  <span class="hljs-comment"># true/false,是否开启插件</span><br>  <span class="hljs-attr">enable_page:</span> <span class="hljs-string">/blog/about/</span> <span class="hljs-comment"># 路由地址，如我的 /blog/是代表网站的root主页。/blog/about/代表的是关于页等等</span><br>  <span class="hljs-attr">user:</span> <span class="hljs-string">helloworld</span> <span class="hljs-comment"># github 或者 gitee 用户名</span><br>  <span class="hljs-attr">layout:</span><br>    <span class="hljs-attr">type:</span> <span class="hljs-string">id</span><br>    <span class="hljs-attr">name:</span> <span class="hljs-string">recent-posts</span><br>    <span class="hljs-attr">index:</span> <span class="hljs-number">0</span><br>  <span class="hljs-attr">githubcalendar_html:</span> <span class="hljs-string">&#x27;&lt;div class=&quot;recent-post-item&quot; style=&quot;width:100%;height:auto;padding:10px;&quot;&gt;&lt;div id=&quot;github_loading&quot; style=&quot;width:10%;height:100%;margin:0 auto;display: block&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot;  viewBox=&quot;0 0 50 50&quot; style=&quot;enable-background:new 0 0 50 50&quot; xml:space=&quot;preserve&quot;&gt;&lt;path fill=&quot;#d0d0d0&quot; d=&quot;M25.251,6.461c-10.318,0-18.683,8.365-18.683,18.683h4.068c0-8.071,6.543-14.615,14.615-14.615V6.461z&quot; transform=&quot;rotate(275.098 25 25)&quot;&gt;&lt;animateTransform attributeType=&quot;xml&quot; attributeName=&quot;transform&quot; type=&quot;rotate&quot; from=&quot;0 25 25&quot; to=&quot;360 25 25&quot; dur=&quot;0.6s&quot; repeatCount=&quot;indefinite&quot;&gt;&lt;/animateTransform&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/div&gt;&lt;div id=&quot;github_container&quot;&gt;&lt;/div&gt;&lt;/div&gt;&#x27;</span><br>  <span class="hljs-attr">pc_minheight:</span> <span class="hljs-string">280px</span><br>  <span class="hljs-attr">mobile_minheight:</span> <span class="hljs-string">0px</span><br>  <span class="hljs-attr">color:</span> <span class="hljs-string">&quot;[&#x27;#ebedf0&#x27;, &#x27;#fdcdec&#x27;, &#x27;#fc9bd9&#x27;, &#x27;#fa6ac5&#x27;, &#x27;#f838b2&#x27;, &#x27;#f5089f&#x27;, &#x27;#c4067e&#x27;, &#x27;#92055e&#x27;, &#x27;#540336&#x27;, &#x27;#48022f&#x27;, &#x27;#30021f&#x27;]&quot;</span><br>  <span class="hljs-attr">api:</span> <span class="hljs-string">https://python-github-calendar-api.vercel.app/api</span><br>  <span class="hljs-comment"># api: https://python-gitee-calendar-api.vercel.app/api</span><br>  <span class="hljs-attr">calendar_js:</span> <span class="hljs-string">https://cdn.jsdelivr.net/gh/Zfour/hexo-github-calendar@1.21/hexo_githubcalendar.js</span><br>  <span class="hljs-attr">plus_style:</span> <span class="hljs-string">&quot;&quot;</span><br></code></pre></td></tr></table></figure><p>更多主题配置请前往：<a href="https://github.com/Zfour/hexo-github-calendar/issues">https://github.com/Zfour/hexo-github-calendar/issues</a><br>也欢迎共享自己的配置和进行修改。</p><p>接下来来简单说明其他一些配置项的含义：</p><h3 id="layout"><a href="#layout" class="headerlink" title="layout"></a>layout</h3><p><strong>参数：</strong>type; （class&amp;id）<br><strong>参数：</strong>name;<br><strong>参数：</strong>index；（数字）<br><strong>含义：</strong>如果说 gihubcalendar 是一幅画，那么这个 layout 就是指定了哪面墙来挂画<br>而在 HTML 的是世界里有两种墙分别 type 为 id 和 class。<br>其中在定义 class 的时候会出现多个 class 的情况，这时就需要使用 index，确定是哪一个。<br>最后墙的名字即是 name;</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;我是墙&quot;</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;recent-posts&quot;</span>&gt;</span><br>  <span class="hljs-comment">&lt;!-- id=&gt;type  recent-posts=&gt;name    --&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;我是画框&quot;</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;我是纸&quot;</span>&gt;</span><br>      <span class="hljs-comment">&lt;!--这里通过js挂载githubcalendar，也就是画画--&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br></code></pre></td></tr></table></figure><h3 id="githubcalendar-html"><a href="#githubcalendar-html" class="headerlink" title="githubcalendar_html"></a>githubcalendar_html</h3><p><strong>参数：</strong>html 模板字段<br><strong>含义：</strong>包含 loading，和挂载容器</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;recent-post-item&quot;</span> <span class="hljs-attr">style</span>=<span class="hljs-string">&quot;width:100%;height:auto;padding:10px;&quot;</span>&gt;</span><br>  <span class="hljs-comment">&lt;!--这个是画框，顾名思义就是借用文章样式给加个框--&gt;</span><br>  <span class="hljs-comment">&lt;!--这个是loading的样式，可自行调整--&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">div</span></span><br><span class="hljs-tag">    <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;github_loading&quot;</span></span><br><span class="hljs-tag">    <span class="hljs-attr">style</span>=<span class="hljs-string">&quot;width:10%;height:100%;margin:0 auto;display: block&quot;</span></span><br><span class="hljs-tag">  &gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">svg</span></span><br><span class="hljs-tag">      <span class="hljs-attr">xmlns</span>=<span class="hljs-string">&quot;http://www.w3.org/2000/svg&quot;</span></span><br><span class="hljs-tag">      <span class="hljs-attr">xmlns:xlink</span>=<span class="hljs-string">&quot;http://www.w3.org/1999/xlink&quot;</span></span><br><span class="hljs-tag">      <span class="hljs-attr">viewBox</span>=<span class="hljs-string">&quot;0 0 50 50&quot;</span></span><br><span class="hljs-tag">      <span class="hljs-attr">style</span>=<span class="hljs-string">&quot;enable-background:new 0 0 50 50&quot;</span></span><br><span class="hljs-tag">      <span class="hljs-attr">xml:space</span>=<span class="hljs-string">&quot;preserve&quot;</span></span><br><span class="hljs-tag">    &gt;</span><br>      <span class="hljs-tag">&lt;<span class="hljs-name">path</span></span><br><span class="hljs-tag">        <span class="hljs-attr">fill</span>=<span class="hljs-string">&quot;#d0d0d0&quot;</span></span><br><span class="hljs-tag">        <span class="hljs-attr">d</span>=<span class="hljs-string">&quot;M25.251,6.461c-10.318,0-18.683,8.365-18.683,18.683h4.068c0-8.071,6.543-14.615,14.615-14.615V6.461z&quot;</span></span><br><span class="hljs-tag">        <span class="hljs-attr">transform</span>=<span class="hljs-string">&quot;rotate(275.098 25 25)&quot;</span></span><br><span class="hljs-tag">      &gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">animateTransform</span></span><br><span class="hljs-tag">          <span class="hljs-attr">attributeType</span>=<span class="hljs-string">&quot;xml&quot;</span></span><br><span class="hljs-tag">          <span class="hljs-attr">attributeName</span>=<span class="hljs-string">&quot;transform&quot;</span></span><br><span class="hljs-tag">          <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;rotate&quot;</span></span><br><span class="hljs-tag">          <span class="hljs-attr">from</span>=<span class="hljs-string">&quot;0 25 25&quot;</span></span><br><span class="hljs-tag">          <span class="hljs-attr">to</span>=<span class="hljs-string">&quot;360 25 25&quot;</span></span><br><span class="hljs-tag">          <span class="hljs-attr">dur</span>=<span class="hljs-string">&quot;0.6s&quot;</span></span><br><span class="hljs-tag">          <span class="hljs-attr">repeatCount</span>=<span class="hljs-string">&quot;indefinite&quot;</span></span><br><span class="hljs-tag">        &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">animateTransform</span>&gt;</span><br>      <span class="hljs-tag">&lt;/<span class="hljs-name">path</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br><br>  <span class="hljs-comment">&lt;!--这个是github_containner容器，也就是纸--&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;github_container&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br></code></pre></td></tr></table></figure><blockquote><p>PS：已经配置过早期版本 GitHubcalendar 的用户该如何适配？<br>其实很简单，首先将原有的 js 去除。然后只需 <code>layout_id</code> 改为原先挂载的 id<code>gitcalendar</code>，再将 <code>html模板字段</code>中的画框的 <code>class=&quot;recent-post-item&quot;</code> 去除，即可适配新版的 npm 版插件。或者你可以采用后续介绍的方案二进行更新。</p></blockquote><h3 id="pc-minheight"><a href="#pc-minheight" class="headerlink" title="pc_minheight"></a>pc_minheight</h3><p><strong>参数：</strong>280px<br><strong>含义：</strong>电脑端插件的最小高度，减少加载带来的视觉偏移</p><h3 id="mobile-minheight"><a href="#mobile-minheight" class="headerlink" title="mobile_minheight"></a>mobile_minheight</h3><p><strong>参数：</strong>0px<br><strong>含义：</strong>手机端插件的最小高度，减少加载带来的视觉偏移</p><h3 id="color"><a href="#color" class="headerlink" title="color"></a>color</h3><p><strong>参数：</strong>“[‘#ebedf0’, ‘#fdcdec’, ‘#fc9bd9’, ‘#fa6ac5’, ‘#f838b2’, ‘#f5089f’, ‘#c4067e’, ‘#92055e’, ‘#540336’, ‘#48022f’, ‘#30021f’]”<br><strong>含义：</strong>calendar 的主题色</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># 以下色调选择喜欢的一行保留即可。其余注释。—————akilar的糖果色</span><br><span class="hljs-attr">color:</span> <span class="hljs-string">&quot;[&#x27;#e4dfd7&#x27;, &#x27;#f9f4dc&#x27;, &#x27;#f7e8aa&#x27;, &#x27;#f7e8aa&#x27;, &#x27;#f8df72&#x27;, &#x27;#fcd217&#x27;, &#x27;#fcc515&#x27;, &#x27;#f28e16&#x27;, &#x27;#fb8b05&#x27;, &#x27;#d85916&#x27;, &#x27;#f43e06&#x27;]&quot;</span> <span class="hljs-comment">#橘黄色调</span><br><span class="hljs-comment"># color: &quot;[&#x27;#ebedf0&#x27;, &#x27;#fdcdec&#x27;, &#x27;#fc9bd9&#x27;, &#x27;#fa6ac5&#x27;, &#x27;#f838b2&#x27;, &#x27;#f5089f&#x27;, &#x27;#c4067e&#x27;, &#x27;#92055e&#x27;, &#x27;#540336&#x27;, &#x27;#48022f&#x27;, &#x27;#30021f&#x27;]&quot; #浅紫色调</span><br><span class="hljs-comment"># color: &quot;[&#x27;#ebedf0&#x27;, &#x27;#f0fff4&#x27;, &#x27;#dcffe4&#x27;, &#x27;#bef5cb&#x27;, &#x27;#85e89d&#x27;, &#x27;#34d058&#x27;, &#x27;#28a745&#x27;, &#x27;#22863a&#x27;, &#x27;#176f2c&#x27;, &#x27;#165c26&#x27;, &#x27;#144620&#x27;]&quot; #翠绿色调</span><br><span class="hljs-comment"># color: &quot;[&#x27;#ebedf0&#x27;, &#x27;#f1f8ff&#x27;, &#x27;#dbedff&#x27;, &#x27;#c8e1ff&#x27;, &#x27;#79b8ff&#x27;, &#x27;#2188ff&#x27;, &#x27;#0366d6&#x27;, &#x27;#005cc5&#x27;, &#x27;#044289&#x27;, &#x27;#032f62&#x27;, &#x27;#05264c&#x27;]&quot; #天青色调</span><br></code></pre></td></tr></table></figure><h3 id="api"><a href="#api" class="headerlink" title="api"></a>api</h3><p><strong>参数：</strong><a href="https://python-github-calendar-api.vercel.app/api">https://python-github-calendar-api.vercel.app/api</a><br>或 <a href="https://python-gitee-calendar-api.vercel.app/api">https://python-gitee-calendar-api.vercel.app/api</a><br><strong>含义：</strong>这里提供的是公用的 api，仅供日常使用，请不要滥用。如果想搭建自用 api，具体的部署方案可看考 <a href="https://github.com/Zfour/python_github_calendar_api">python_github_calendar_api</a> 及 <a href="https://github.com/Zfour/python_gitee_calendar_api">python_gitee_calendar_api</a> 的文档说明，这里不多加赘述。</p><h3 id="calendar-js"><a href="#calendar-js" class="headerlink" title="calendar_js"></a>calendar_js</h3><p><strong>参数：</strong><a href="https://cdn.jsdelivr.net/gh/Zfour/hexo-github-calendar@1.21/hexo_githubcalendar.js">https://cdn.jsdelivr.net/gh/Zfour/hexo-github-calendar@1.21/hexo_githubcalendar.js</a><br><strong>含义：</strong>jsd 加速的 js，将 github calendar 挂载入容器中<br><strong>目前已知 bug：</strong>在 1.21 适配 retina 屏幕后虽解决了模糊问题，但部分用户的 tooltip 会出现数据错误。降级到 @1.16 使用即可解决。</p><h3 id="plus-style"><a href="#plus-style" class="headerlink" title="plus_style"></a>plus_style</h3><p><strong>参数：</strong>“”<br><strong>含义：</strong>提供可自定义的 style</p><h2 id="挂载"><a href="#挂载" class="headerlink" title="挂载"></a>挂载</h2><p>如果你想在友链或者个人介绍挂载，你可以在 md 中增加墙 —— 也就是具有某一的 id 的 div。因为是在 md 中所以通过去掉 class 隐藏画框的样式，即可。同时需要调整 <code>enable_page</code> 来限定展示的页面。如我的配置如下，同理可扩展至其他主题。总的来说就是，如果你是 butterfly 主题，你需要修改用户名即可，如果你是其他主题用户，你可以尝试使用墙 &gt; 画框 &gt; 画的方式挂载，也可以通过修改主题模板来挂载。如<a href="https://imciraos.com/posts/353f0aee/">给萌典主题加上 git-calendar</a> 的方法。</p><img src="/blog/posts/91dc324b/image-20210419092440737.png" class="" title="image-20210419092440737"><p>执行<code>hexo clean &amp;&amp; hexo g &amp;&amp; hexo s</code>，你就能看到效果惹。</p><blockquote><p>参考：<a href="https://zfe.space/post/hexo-githubcalendar.html">https://zfe.space/post/hexo-githubcalendar.html</a></p></blockquote>]]></content>
    
    
    
    <tags>
      
      <tag>Hexo</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>一起学可视化</title>
    <link href="/blog/posts/7d474f9c.html"/>
    <url>/blog/posts/7d474f9c.html</url>
    
    <content type="html"><![CDATA[<h2 id="什么是可视化"><a href="#什么是可视化" class="headerlink" title="什么是可视化"></a>什么是可视化</h2><p>可视化用一句话来说，本质上就是将数据信息组织起来后，以图形的方式呈现出来。在 Web 上，图形通常是通过浏览器绘制的。现代浏览器是一个复杂的系统，其中负责绘制图形的部分是渲染引擎。渲染引擎绘制图形的方式，我总结了一下，大体上有 4 种。</p><h3 id="HTML-CSS"><a href="#HTML-CSS" class="headerlink" title="HTML+CSS"></a>HTML+CSS</h3><p>这种方式通常用来呈现普通的 Web 网页。</p><h3 id="SVG"><a href="#SVG" class="headerlink" title="SVG"></a>SVG</h3><p>SVG 和传统的 HTML+CSS 的绘图方式差别不大。只不过，HTML 元素在绘制矢量图形方面的能力有些不足（我们后面会讲到），而 SVG 恰好弥补了这方面的缺陷。</p><h3 id="Canvas2D"><a href="#Canvas2D" class="headerlink" title="Canvas2D"></a>Canvas2D</h3><p>这是浏览器提供的 Canvas API 中的其中一种上下文，使用它可以非常方便地绘制出基础的几何图形。在可视化中，Canvas 比较常用。</p><h3 id="WebGL"><a href="#WebGL" class="headerlink" title="WebGL"></a>WebGL</h3><p>这是浏览器提供的 Canvas API 中的另一种上下文，它是 OpenGL ES 规范在 Web 端的实现。我们可以通过它，用 GPU 渲染各种复杂的 2D 和 3D 图形。值得一提的是，WebGL 利用了 GPU 并行处理的特性，这让它在处理大量数据展现的时候，性能大大优于前 3 种绘图方式。因此，在可视化的应用中，一些数据量大、视觉效果要求高的特殊场景，使用 WebGL 渲染是一种比较合适的选择。</p>]]></content>
    
    
    
    <tags>
      
      <tag>可视化</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>重学前端总结</title>
    <link href="/blog/posts/c2d307e0.html"/>
    <url>/blog/posts/c2d307e0.html</url>
    
    <content type="html"><![CDATA[<h2 id="JavaScript"><a href="#JavaScript" class="headerlink" title="JavaScript"></a>JavaScript</h2><h3 id="关于类型，有哪些你不知道的细节？"><a href="#关于类型，有哪些你不知道的细节？" class="headerlink" title="关于类型，有哪些你不知道的细节？"></a>关于类型，有哪些你不知道的细节？</h3><p>JavaScript 语言的每一个值都属于某一种数据类型。JavaScript 语言规定了 7 种语言类型。语言类型广泛用于变量、函数参数、表达式、函数返回值等场合。根据最新的语言标准，这 7 种语言类型是：</p><ul><li><code>Undefined</code>、<code>Null</code>、<code>Boolean</code>、<code>String</code>、<code>Number</code>、<code>Symbol</code>。（ES2020新增了<code>Bigint</code>）</li><li><code>Object</code>。</li></ul><h3 id="Undefined、Null"><a href="#Undefined、Null" class="headerlink" title="Undefined、Null"></a>Undefined、Null</h3><p>任何变量在赋值前是 <code>Undefined</code> 类型、值为 <code>undefined</code>，一般我们可以用全局变量 <code>undefined</code>（就是名为 <code>undefined</code> 的这个变量）来表达这个值，或者 <code>void</code> 运算来把任意一个表达式变成 <code>undefined</code> 值。<br><code>Undefined</code> 跟 <code>Null</code> 有一定的表意差别，<code>Null</code> 表示的是：“定义了但是为空”。所以，在实际编程时，我们一般不会把变量赋值为 <code>undefined</code>，这样可以保证所有值为 <code>undefined</code> 的变量，都是从未赋值的自然状态。<br><code>Null</code> 类型也只有一个值，就是 <code>null</code>，它的语义表示空值，与 <code>undefined</code> 不同，<code>null</code> 是 <code>JavaScript</code> 关键字，所以在任何代码中，你都可以放心用 <code>null</code> 关键字来获取 <code>null</code> 值。</p><h3 id="Boolean"><a href="#Boolean" class="headerlink" title="Boolean"></a>Boolean</h3><p>Boolean 类型有两个值， true 和 false，它用于表示逻辑意义上的真和假，同样有关键字 true 和 false 来表示两个值。这个类型很简单，我就不做过多介绍了。</p><h3 id="String"><a href="#String" class="headerlink" title="String"></a>String</h3><p>我们来看看字符串是否有最大长度。<code>String</code> 用于表示文本数据。<code>String</code> 有最大长度是 2^53 - 1，这在一般开发中都是够用的，但是有趣的是，这个所谓最大长度，并不完全是你理解中的字符数。因为 <code>String</code> 的意义并非“字符串”，而是字符串的 <code>UTF16</code> 编码，我们字符串的操作 <code>charAt</code>、<code>charCodeAt</code>、<code>length</code> 等方法针对的都是 <code>UTF16</code> 编码。所以，字符串的最大长度，实际上是受字符串的编码长度影响的。Note：现行的字符集国际标准，字符是以 <code>Unicode</code> 的方式表示的，每一个 <code>Unicode</code> 的码点表示一个字符，理论上，<code>Unicode</code> 的范围是无限的。UTF 是 <code>Unicode</code> 的编码方式，规定了码点在计算机中的表示方法，常见的有 <code>UTF16</code> 和 UTF8。 <code>Unicode</code> 的码点通常用 U+??? 来表示，其中 ??? 是十六进制的码点值。 0-65536（U+0000 - U+FFFF）的码点被称为基本字符区域（BMP）。<code>JavaScript</code> 中的字符串是永远无法变更的，一旦字符串构造出来，无法用任何方式改变字符串的内容，所以字符串具有值类型的特征。<code>JavaScript</code> 字符串把每个 <code>UTF16</code> 单元当作一个字符来处理，所以处理非 BMP（超出 U+0000 - U+FFFF 范围）的字符时，你应该格外小心。<code>JavaScript</code> 这个设计继承自 Java，最新标准中是这样解释的，这样设计是为了“性能和尽可能实现起来简单”。因为现实中很少用到 BMP 之外的字符。</p><h3 id="Number"><a href="#Number" class="headerlink" title="Number"></a>Number</h3><p>下面，我们来说说 <code>Number</code> 类型。<code>Number</code> 类型表示我们通常意义上的“数字”。这个数字大致对应数学中的有理数，当然，在计算机中，我们有一定的精度限制。<code>JavaScript</code> 中的 <code>Number</code> 类型有 18437736874454810627(即 2^64-2^53+3) 个值。<code>JavaScript</code> 中的 <code>Number</code> 类型基本符合 IEEE 754-2008 规定的双精度浮点数规则，但是 <code>JavaScript</code> 为了表达几个额外的语言场景（比如不让除以 0 出错，而引入了无穷大的概念），规定了几个例外情况：NaN，占用了 9007199254740990，这原本是符合 IEEE 规则的数字；Infinity，无穷大；-Infinity，负无穷大。</p><p>根据双精度浮点数的定义，<code>Number</code> 类型中有效的整数范围是 -0x1fffffffffffff 至 0x1fffffffffffff，所以 <code>Number</code> 无法精确表示此范围外的整数。同样根据浮点数的定义，非整数的 <code>Number</code> 类型无法用 ==（=== 也不行） 来比较，一段著名的代码，这也正是我们第三题的问题，为什么在 <code>JavaScript</code> 中，0.1+0.2 不能 =0.3：</p><p>正确的比较方法是使用 <code>JavaScript</code> 提供的最小精度值：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-built_in">console</span>.log( <span class="hljs-built_in">Math</span>.abs(<span class="hljs-number">0.1</span> + <span class="hljs-number">0.2</span> - <span class="hljs-number">0.3</span>) &lt;= <span class="hljs-built_in">Number</span>.EPSILON);<br></code></pre></td></tr></table></figure><h3 id="Symbol"><a href="#Symbol" class="headerlink" title="Symbol"></a>Symbol</h3><p>Symbol 是 ES6 中引入的新类型，它是一切非字符串的对象 key 的集合，在 ES6 规范中，整个对象系统被用 Symbol 重塑。在后面的文章中，我会详细叙述 Symbol 跟对象系统。这里我们只介绍 Symbol 类型本身：它有哪些部分，它表示什么意思，以及如何创建 Symbol 类型。Symbol 可以具有字符串类型的描述，但是即使描述相同，Symbol 也不相等。</p><h3 id="Object"><a href="#Object" class="headerlink" title="Object"></a>Object</h3><p>Object 是 JavaScript 中最复杂的类型，也是 JavaScript 的核心机制之一。Object 表示对象的意思，它是一切有形和无形物体的总称。<br>在 JavaScript 中，对象的定义是“属性的集合”。属性分为数据属性和访问器属性，二者都是 key-value 结构，key 可以是字符串或者 Symbol 类型。<br>我们必须认识到 3 与 new Number(3) 是完全不同的值，它们一个是 Number 类型， 一个是对象类型。<br>Number、String 和 Boolean，三个构造器是两用的，当跟 new 搭配时，它们产生对象，当直接调用时，它们表示强制类型转换。Symbol 函数比较特殊，直接用 new 调用它会抛出错误，但它仍然是 Symbol 对象的构造器。</p><h2 id="HTML-amp-CSS"><a href="#HTML-amp-CSS" class="headerlink" title="HTML&amp;CSS"></a>HTML&amp;CSS</h2><h2 id="浏览器实现原理与API"><a href="#浏览器实现原理与API" class="headerlink" title="浏览器实现原理与API"></a>浏览器实现原理与API</h2><h2 id="前端综合应用"><a href="#前端综合应用" class="headerlink" title="前端综合应用"></a>前端综合应用</h2><blockquote><p>极好的Javascript面经<br><a href="https://github.com/nieyafei/front-end-interview-js">https://github.com/nieyafei/front-end-interview-js</a><br>前端面试interview的Js题目收集 <a href="https://github.com/jimuyouyou/node-interview-questions">https://github.com/jimuyouyou/node-interview-questions</a><br>Node.js面试题，侧重后端应用与对Node核心的理解 <a href="https://github.com/paddingme/Front-end-Web-Development-Interview-Question">https://github.com/paddingme/Front-end-Web-Development-Interview-Question</a><br>前端开发面试题大收集 <a href="https://github.com/huruji/FE-Interview">https://github.com/huruji/FE-Interview</a><br>前端面试题大合集 <a href="https://github.com/CyC2018/CS-Notes">https://github.com/CyC2018/CS-Notes</a><br>技术面试必备基础知识 <a href="https://github.com/doocs/coding-interview">https://github.com/doocs/coding-interview</a><br>代码面试题集 <a href="https://zhuanlan.zhihu.com/p/57302141">https://zhuanlan.zhihu.com/p/57302141</a><br>大厂前端高频面试问题与答案精选 <a href="https://juejin.im/post/5c8f30606fb9a070ef60996d">https://juejin.im/post/5c8f30606fb9a070ef60996d</a><br>寒冬中的前端社招面试 <a href="https://zhuanlan.zhihu.com/p/53703176">https://zhuanlan.zhihu.com/p/53703176</a> Vue问得最多的面试题 <a href="https://juejin.im/post/5d13436f6fb9a07eca698ba0">https://juejin.im/post/5d13436f6fb9a07eca698ba0</a><br>你要的Vue面试题都在这里 <a href="https://juejin.im/post/5d153267e51d4510624f9809">https://juejin.im/post/5d153267e51d4510624f9809</a><br>vue 220+个知识点（面试题）为你保驾护航 <a href="https://juejin.im/entry/5d06ce32e51d4510a50335bd">https://juejin.im/entry/5d06ce32e51d4510a50335bd</a><br>[译] 送你 43 道 JavaScript 面试题 <a href="https://juejin.im/post/5d1d52aff265da1bb2774de0">https://juejin.im/post/5d1d52aff265da1bb2774de0</a><br>面试官到底想看什么样的简历？ <a href="https://github.com/Advanced-Frontend/Daily-Interview-Question/blob/master/datum/summary.md">https://github.com/Advanced-Frontend/Daily-Interview-Question/blob/master/datum/summary.md</a><br>前端面试题及答案汇总 <a href="https://github.com/pwstrick/daily">https://github.com/pwstrick/daily</a><br>一份搜集的前端面试题目清单+面试相关的文章 <a href="https://segmentfault.com/a/1190000020181662">https://segmentfault.com/a/1190000020181662</a><br>Vue面试中，经常会被问到的面试题/知识点(2019改进版) <a href="https://juejin.im/post/5d7596055188253e4b2f0c29">https://juejin.im/post/5d7596055188253e4b2f0c29</a><br>36 个JS 面试题为你助力金九银十(面试必读) <a href="https://github.com/0voice/interview_internal_reference">https://github.com/0voice/interview_internal_reference</a><br>2019年最新总结，阿里，腾讯，百度，美团，头条等技术面试题目，以及答案，专家出题人分析汇总 <a href="https://juejin.im/entry/5db1c186518825646b111a4d">https://juejin.im/entry/5db1c186518825646b111a4d</a><br>2019年最新经典web前端面试题超全面细节 <a href="https://q.shanyue.tech/fe/">https://q.shanyue.tech/fe/</a> 前端面试题小记 <a href="https://q.shanyue.tech/interview.html">https://q.shanyue.tech/interview.html</a><br>前端大厂面经大全集 <a href="https://q.shanyue.tech/base/">https://q.shanyue.tech/base/</a><br>计算机基础面试题小计 <a href="https://fecommunity.github.io/front-end-interview/">https://fecommunity.github.io/front-end-interview/</a><br>前端工程师面试宝典 <a href="https://juejin.im/post/6893856813247266823">https://juejin.im/post/6893856813247266823</a> </p></blockquote>]]></content>
    
    
    
    <tags>
      
      <tag>JavaScript</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>hexo的fluid主题添加瀑布流懒加载相册功能</title>
    <link href="/blog/posts/798ba833.html"/>
    <url>/blog/posts/798ba833.html</url>
    
    <content type="html"><![CDATA[<div class="note note-warning">相册演示地址:<a style="color:red" href="https://www.gishai.top/blog/photos/">https://www.gishai.top/blog/photos/</a> ,当前演示环境基于hexo===5.2.0 , fluid===1.8.7,很大程度上参考了醉里挑灯赏猫的<a href="https://blog.dlzhang.com/posts/31/" style="color:red">Hexo NexT 博客增加瀑布流相册页面</a>这篇博客!在此感谢班班提供的帮助!!</div><h2 id="创建相册页面"><a href="#创建相册页面" class="headerlink" title="创建相册页面"></a>创建相册页面</h2><p>新建相册页 <code>hexo new page photos</code>,编辑 <code>/source/photos/index.md</code>，输入以下内容：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs html">---<br>title: photos<br>date: 2020-12-30 19:04:03<br>layout: photo<br>---<br><br><span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css"></span><br><span class="css"><span class="hljs-selector-class">.ImageGrid</span> &#123;</span><br><span class="css">  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;</span><br><span class="css">  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">1040px</span>;</span><br><span class="css">  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;</span><br><span class="css">  <span class="hljs-attribute">text-align</span>: center;</span><br><span class="css">&#125;</span><br><span class="css"><span class="hljs-selector-class">.card</span> &#123;</span><br><span class="css">  <span class="hljs-attribute">overflow</span>: hidden;</span><br><span class="css">  <span class="hljs-attribute">transition</span>: .<span class="hljs-number">3s</span> ease-in-out;</span><br><span class="css">  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;</span><br><span class="css">  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#efefef</span>;</span><br><span class="css">  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1.4px</span>;</span><br><span class="css">&#125;</span><br><span class="css"><span class="hljs-selector-class">.ImageInCard</span> <span class="hljs-selector-tag">img</span> &#123;</span><br><span class="css">  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;</span><br><span class="css">  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;</span><br><span class="css">  <span class="hljs-attribute">width</span>:<span class="hljs-number">100%</span>;</span><br><span class="css">  <span class="hljs-attribute">height</span>:<span class="hljs-number">100%</span>;</span><br><span class="css">&#125;</span><br><span class="css"><span class="hljs-keyword">@media</span> (<span class="hljs-attribute">prefers-color-scheme</span>: dark) &#123;</span><br><span class="css">  <span class="hljs-selector-class">.card</span> &#123;<span class="hljs-attribute">background-color</span>: <span class="hljs-number">#333</span>;&#125;</span><br><span class="css">&#125;</span><br><span class="css"></span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span><br><br><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;imageTab&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;ImageGrid&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br><br></code></pre></td></tr></table></figure><h2 id="处理图片信息"><a href="#处理图片信息" class="headerlink" title="处理图片信息"></a>处理图片信息</h2><p>为了加快图片的加载速度,我使用<code>GitHub</code> +<code>jsDelivr</code>的方式，<strong>网上有许多的教程，此处不再演示。</strong>文件夹结构如图:</p><img src="/blog/posts/798ba833/image-20210110205814315.png" class="" title="image-20210110205814315"><p>主要功能是使用<code>image-size</code>( <code>npm i -S image-size</code>安装)访问照片文件夹，获取每张照片的大小和文件名，并生成对应的 <code>json</code> 文件：</p><p><code>cnpm i</code>安装之后,把照片放在目录后，执行以下命令：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">node gallery/create.js<br></code></pre></td></tr></table></figure><p>如果报错，请注意检查保存本地照片的文件夹里有没有非图片类文件，特别是要删除如 <code>.DS_Store</code> 这样的隐藏文件。<code>json</code> 文件样例如下：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs json">[<br>  &#123;<br>    <span class="hljs-attr">&quot;name&quot;</span>: <span class="hljs-string">&quot;广州一游&quot;</span>,<br>    <span class="hljs-attr">&quot;children&quot;</span>: [<br>      <span class="hljs-string">&quot;1080.1440 圣心大教堂.jpg&quot;</span>,<br>      <span class="hljs-string">&quot;1080.1440 广州塔顶夜晚景色.jpg&quot;</span>,<br>      <span class="hljs-string">&quot;1080.1440 广州塔顶夜晚景色2.jpg&quot;</span>,<br>      <span class="hljs-string">&quot;1080.1440 晚上广州塔.jpg&quot;</span>,<br>      <span class="hljs-string">&quot;1080.1443 白天广州塔.jpg&quot;</span><br>    ]<br>  &#125;,<br>  &#123;<br>    <span class="hljs-attr">&quot;name&quot;</span>: <span class="hljs-string">&quot;澳门游玩&quot;</span>,<br>    <span class="hljs-attr">&quot;children&quot;</span>: [<br>      <span class="hljs-string">&quot;1080.1443 夜晚澳门巴黎铁塔.jpg&quot;</span>,<br>      <span class="hljs-string">&quot;1443.1080 微信图片_20210108213615.jpg&quot;</span>,<br>      <span class="hljs-string">&quot;1443.1080 微信图片_20210108213635.jpg&quot;</span>,<br>      <span class="hljs-string">&quot;1443.1080 微信图片_20210108213645.jpg&quot;</span>,<br>      <span class="hljs-string">&quot;1443.1080 微信图片_20210108213707.jpg&quot;</span>,<br>      <span class="hljs-string">&quot;1080.1443 白天澳门巴黎铁塔.jpg&quot;</span><br>    ]<br>  &#125;<br>]<br></code></pre></td></tr></table></figure><p>将<code>photos.json</code>拷贝到博客目录下的<code>photos</code></p><img src="/blog/posts/798ba833/image-20210110210639345.png" class="" title="image-20210110210639345"><h2 id="加载-js和css文件"><a href="#加载-js和css文件" class="headerlink" title="加载 js和css文件"></a>加载 js和css文件</h2><p>在 <code>/source/js/</code> 目录下创建 <code>photoWall.js</code>：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">var</span> imgDataPath = <span class="hljs-string">&quot;/blog/photos/photos.json&quot;</span>; <span class="hljs-comment">//图片名称高宽信息json文件路径</span><br><span class="hljs-keyword">var</span> imgPath = <span class="hljs-string">&quot;https://cdn.jsdelivr.net/gh/Cenergy/images/gallery/&quot;</span>; <span class="hljs-comment">//图片访问路径</span><br><span class="hljs-keyword">var</span> imgMaxNum = <span class="hljs-number">50</span>; <span class="hljs-comment">//图片显示数量</span><br><br><span class="hljs-keyword">var</span> windowWidth =<br>  <span class="hljs-built_in">window</span>.innerWidth ||<br>  <span class="hljs-built_in">document</span>.documentElement.clientWidth ||<br>  <span class="hljs-built_in">document</span>.body.clientWidth;<br><span class="hljs-keyword">if</span> (windowWidth &lt; <span class="hljs-number">768</span>) &#123;<br>  <span class="hljs-keyword">var</span> imageWidth = <span class="hljs-number">145</span>; <span class="hljs-comment">//图片显示宽度(手机端)</span><br>&#125; <span class="hljs-keyword">else</span> &#123;<br>  <span class="hljs-keyword">var</span> imageWidth = <span class="hljs-number">250</span>; <span class="hljs-comment">//图片显示宽度</span><br>&#125;<br><br><span class="hljs-keyword">const</span> photo = &#123;<br>  <span class="hljs-attr">page</span>: <span class="hljs-number">1</span>,<br>  <span class="hljs-attr">offset</span>: imgMaxNum,<br>  <span class="hljs-attr">init</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;<br>    <span class="hljs-keyword">var</span> that = <span class="hljs-built_in">this</span>;<br>    $.getJSON(imgDataPath, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">data</span>) </span>&#123;<br>      that.render(that.page, data);<br>      <span class="hljs-comment">//that.scroll(data);</span><br>      that.eventListen(data);<br>    &#125;);<br>  &#125;,<br>  <span class="hljs-function"><span class="hljs-title">constructHtml</span>(<span class="hljs-params">options</span>)</span> &#123;<br>    <span class="hljs-keyword">const</span> &#123;<br>      imageWidth,<br>      imageX,<br>      imageY,<br>      name,<br>      imgPath,<br>      imgName,<br>      imgNameWithPattern,<br>    &#125; = options;<br>    <span class="hljs-keyword">const</span> htmlEle = <span class="hljs-string">`&lt;div class=&quot;card lozad&quot; style=&quot;width:<span class="hljs-subst">$&#123;imageWidth&#125;</span>px&quot;&gt;</span><br><span class="hljs-string">                  &lt;div class=&quot;ImageInCard&quot; style=&quot;height:<span class="hljs-subst">$&#123;</span></span><br><span class="hljs-subst"><span class="hljs-string">                    (imageWidth * imageY) / imageX</span></span><br><span class="hljs-subst"><span class="hljs-string">                  &#125;</span>px&quot;&gt;</span><br><span class="hljs-string">                    &lt;a data-fancybox=&quot;gallery&quot; href=&quot;<span class="hljs-subst">$&#123;imgPath&#125;</span><span class="hljs-subst">$&#123;name&#125;</span>/<span class="hljs-subst">$&#123;imgNameWithPattern&#125;</span>&quot;</span><br><span class="hljs-string">                          data-caption=&quot;<span class="hljs-subst">$&#123;imgName&#125;</span>&quot; title=&quot;<span class="hljs-subst">$&#123;imgName&#125;</span>&quot;&gt;</span><br><span class="hljs-string">                            &lt;img  class=&quot;lazyload&quot; data-src=&quot;<span class="hljs-subst">$&#123;imgPath&#125;</span><span class="hljs-subst">$&#123;name&#125;</span>/<span class="hljs-subst">$&#123;imgNameWithPattern&#125;</span>&quot;</span><br><span class="hljs-string">                            src=&quot;data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==&quot;</span><br><span class="hljs-string">                            onload=&quot;lzld(this)&quot;</span><br><span class="hljs-string">                            lazyload=&quot;auto&quot;&gt;</span><br><span class="hljs-string">                        &lt;/a&gt;</span><br><span class="hljs-string">                  &lt;/div&gt;</span><br><span class="hljs-string">                &lt;/div&gt;`</span>;<br>    <span class="hljs-keyword">return</span> htmlEle;<br>  &#125;,<br>  <span class="hljs-attr">render</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">page, data = []</span>) </span>&#123;<br>    <span class="hljs-built_in">this</span>.data = data;<br>    <span class="hljs-keyword">if</span> (!data.length) <span class="hljs-keyword">return</span>;<br>    <span class="hljs-keyword">var</span> html,<br>      imgNameWithPattern,<br>      imgName,<br>      imageSize,<br>      imageX,<br>      imageY,<br>      li = <span class="hljs-string">&quot;&quot;</span>;<br><br>    <span class="hljs-keyword">let</span> liHtml = <span class="hljs-string">&quot;&quot;</span>;<br>    <span class="hljs-keyword">let</span> contentHtml = <span class="hljs-string">&quot;&quot;</span>;<br><br>    data.forEach(<span class="hljs-function">(<span class="hljs-params">item, index</span>) =&gt;</span> &#123;<br>      <span class="hljs-keyword">const</span> activeClass = index === <span class="hljs-number">0</span> ? <span class="hljs-string">&quot;active&quot;</span> : <span class="hljs-string">&quot;&quot;</span>;<br>      liHtml += <span class="hljs-string">`&lt;li class=&quot;nav-item&quot; role=&quot;presentation&quot;&gt;</span><br><span class="hljs-string">          &lt;a class=&quot;nav-link <span class="hljs-subst">$&#123;activeClass&#125;</span> photo-tab&quot; id=&quot;home-tab&quot; photo-uuid=&quot;<span class="hljs-subst">$&#123;item.name&#125;</span>&quot; data-toggle=&quot;tab&quot; href=&quot;#<span class="hljs-subst">$&#123;item.name&#125;</span>&quot;  role=&quot;tab&quot; aria-controls=&quot;<span class="hljs-subst">$&#123;item.name&#125;</span>&quot; aria-selected=&quot;true&quot;&gt;<span class="hljs-subst">$&#123;item.name&#125;</span>&lt;/a&gt;</span><br><span class="hljs-string">        &lt;/li&gt;`</span>;<br>    &#125;);<br>    <span class="hljs-keyword">const</span> [initData = &#123;&#125;] = data;<br>    <span class="hljs-keyword">const</span> &#123; children = [],name &#125; = initData;<br>    children.forEach(<span class="hljs-function">(<span class="hljs-params">item, index</span>) =&gt;</span> &#123;<br>      imgNameWithPattern = item.split(<span class="hljs-string">&quot; &quot;</span>)[<span class="hljs-number">1</span>];<br>      imgName = imgNameWithPattern.split(<span class="hljs-string">&quot;.&quot;</span>)[<span class="hljs-number">0</span>];<br>      imageSize = item.split(<span class="hljs-string">&quot; &quot;</span>)[<span class="hljs-number">0</span>];<br>      imageX = imageSize.split(<span class="hljs-string">&quot;.&quot;</span>)[<span class="hljs-number">0</span>];<br>      imageY = imageSize.split(<span class="hljs-string">&quot;.&quot;</span>)[<span class="hljs-number">1</span>];<br>      <span class="hljs-keyword">let</span> imgOptions = &#123;<br>        imageWidth,<br>        imageX,<br>        imageY,<br>        name,<br>        imgName,<br>        imgPath,<br>        imgNameWithPattern,<br>      &#125;;<br>      li += <span class="hljs-built_in">this</span>.constructHtml(imgOptions);<br>    &#125;);<br>    contentHtml += <span class="hljs-string">` &lt;div class=&quot;tab-pane fade show active&quot;  role=&quot;tabpanel&quot; aria-labelledby=&quot;home-tab&quot;&gt;<span class="hljs-subst">$&#123;li&#125;</span>&lt;/div&gt;`</span>;<br><br>    <span class="hljs-keyword">const</span> ulHtml = <span class="hljs-string">`&lt;ul class=&quot;nav nav-tabs&quot; id=&quot;myTab&quot; role=&quot;tablist&quot;&gt;<span class="hljs-subst">$&#123;liHtml&#125;</span>&lt;/ul&gt;`</span>;<br>    <span class="hljs-keyword">const</span> tabContent = <span class="hljs-string">`&lt;div class=&quot;tab-content&quot; id=&quot;myTabContent&quot;&gt;<span class="hljs-subst">$&#123;contentHtml&#125;</span>&lt;/div&gt;`</span>;<br><br>    $(<span class="hljs-string">&quot;#imageTab&quot;</span>).append(ulHtml);<br>    $(<span class="hljs-string">&quot;.ImageGrid&quot;</span>).append(tabContent);<br>    <span class="hljs-built_in">this</span>.minigrid();<br>  &#125;,<br>  <span class="hljs-attr">eventListen</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">data</span>) </span>&#123;<br>    <span class="hljs-keyword">let</span> self = <span class="hljs-built_in">this</span>;<br>    <span class="hljs-keyword">var</span> html,<br>      imgNameWithPattern,<br>      imgName,<br>      imageSize,<br>      imageX,<br>      imageY,<br>      li = <span class="hljs-string">&quot;&quot;</span>;<br>    $(<span class="hljs-string">&#x27;a[data-toggle=&quot;tab&quot;]&#x27;</span>).on(<span class="hljs-string">&quot;shown.bs.tab&quot;</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) </span>&#123;<br>      $(<span class="hljs-string">&quot;.ImageGrid&quot;</span>).empty();<br>      <span class="hljs-keyword">const</span> selectId = $(e.target).attr(<span class="hljs-string">&quot;photo-uuid&quot;</span>);<br>      <span class="hljs-keyword">const</span> selectedData = data.find(<span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> data.name === selectId) || &#123;&#125;;<br>      <span class="hljs-keyword">const</span> &#123; children,name &#125; = selectedData;<br>      <span class="hljs-keyword">let</span> li = <span class="hljs-string">&quot;&quot;</span>;<br>      children.forEach(<span class="hljs-function">(<span class="hljs-params">item, index</span>) =&gt;</span> &#123;<br>        imgNameWithPattern = item.split(<span class="hljs-string">&quot; &quot;</span>)[<span class="hljs-number">1</span>];<br>        imgName = imgNameWithPattern.split(<span class="hljs-string">&quot;.&quot;</span>)[<span class="hljs-number">0</span>];<br>        imageSize = item.split(<span class="hljs-string">&quot; &quot;</span>)[<span class="hljs-number">0</span>];<br>        imageX = imageSize.split(<span class="hljs-string">&quot;.&quot;</span>)[<span class="hljs-number">0</span>];<br>        imageY = imageSize.split(<span class="hljs-string">&quot;.&quot;</span>)[<span class="hljs-number">1</span>];<br>        <span class="hljs-keyword">let</span> imgOptions = &#123;<br>          imageWidth,<br>          imageX,<br>          imageY,<br>          name,<br>          imgName,<br>          imgPath,<br>          imgNameWithPattern,<br>        &#125;;<br>        li += self.constructHtml(imgOptions);<br>      &#125;);<br>      $(<span class="hljs-string">&quot;.ImageGrid&quot;</span>).append(li);<br>      self.minigrid();<br>    &#125;);<br>  &#125;,<br>  <span class="hljs-attr">minigrid</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;<br>    <span class="hljs-keyword">var</span> grid = <span class="hljs-keyword">new</span> Minigrid(&#123;<br>      <span class="hljs-attr">container</span>: <span class="hljs-string">&quot;.ImageGrid&quot;</span>,<br>      <span class="hljs-attr">item</span>: <span class="hljs-string">&quot;.card&quot;</span>,<br>      <span class="hljs-attr">gutter</span>: <span class="hljs-number">12</span>,<br>    &#125;);<br>    grid.mount();<br>    $(<span class="hljs-built_in">window</span>).resize(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;<br>      grid.mount();<br>    &#125;);<br>  &#125;,<br>&#125;;<br>photo.init();<br><br></code></pre></td></tr></table></figure><p>然后使用注册器将需要的<code>js</code>,<code>css</code>注入,在<code>scripts/injector.js</code>(如没有,则创建)中输入以下内容:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> &#123; <span class="hljs-attr">root</span>: siteRoot = <span class="hljs-string">&quot;/&quot;</span> &#125; = hexo.config;<br><span class="hljs-comment">// layout为photo的时候导入这些js与css</span><br>hexo.extend.injector.register(<br>  <span class="hljs-string">&quot;body_end&quot;</span>,<br>  <span class="hljs-string">`</span><br><span class="hljs-string">  &lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.staticfile.org/fancybox/3.5.7/jquery.fancybox.min.css&quot;&gt;</span><br><span class="hljs-string">  &lt;script src=&quot;//cdn.jsdelivr.net/npm/minigrid@3.1.1/dist/minigrid.min.js&quot;&gt;&lt;/script&gt;</span><br><span class="hljs-string">  &lt;script src=&quot;https://cdn.staticfile.org/fancybox/3.5.7/jquery.fancybox.min.js&quot;&gt;&lt;/script&gt;</span><br><span class="hljs-string">&lt;script src=&quot;https://cdn.bootcdn.net/ajax/libs/lazyloadjs/3.2.2/lazyload.js&quot;&gt;&lt;/script&gt;</span><br><span class="hljs-string">    &lt;script defer src=&quot;<span class="hljs-subst">$&#123;siteRoot&#125;</span>js/photoWall.js&quot;&gt;&lt;/script&gt;`</span>,<br>  <span class="hljs-string">&quot;photo&quot;</span><br>);<br></code></pre></td></tr></table></figure><p>至此,已经能看到加载出图片了,但是假如不使用cdn或者其他懒加载策略的话会感觉很慢!!</p><blockquote><p>参考:<a href="https://blog.dlzhang.com/posts/31/">https://blog.dlzhang.com/posts/31/</a></p></blockquote>]]></content>
    
    
    
    <tags>
      
      <tag>Hexo</tag>
      
      <tag>Fluid</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>23种经典的设计模式</title>
    <link href="/blog/posts/d673e1b7.html"/>
    <url>/blog/posts/d673e1b7.html</url>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>23 种经典设计模式共分为 3 种类型，分别是<strong>创建型</strong>、<strong>结构型</strong>和<strong>行为型</strong></p><img src="/blog/posts/d673e1b7/image-20201231132923920.png" class="" title="设计模式"><h2 id="创建型设计模式"><a href="#创建型设计模式" class="headerlink" title="创建型设计模式"></a>创建型设计模式</h2><p>创建型设计模式包括：<strong>单例模式</strong>、<strong>工厂模式</strong>、<strong>建造者模式</strong>、<strong>原型模式</strong>。它主要解决对象的创建问题，封装复杂的创建过程，解耦对象的创建代码和使用代码。</p><h3 id="单例模式"><a href="#单例模式" class="headerlink" title="单例模式"></a>单例模式</h3><p><em>单例模式</em>用来创建全局唯一的对象。一个类只允许创建一个对象（或者叫实例），那这个类就是一个单例类，这种设计模式就叫作单例模式。单例有几种经典的实现方式，它们分别是：<code>饿汉式</code>、<code>懒汉式</code>、<code>双重检测</code>、<code>静态内部类</code>、<code>枚举</code>。</p><p>尽管单例是一个很常用的设计模式，在实际的开发中，我们也确实经常用到它，但是，有些人认为单例是一种反模式（anti-pattern），并不推荐使用，主要的理由有以下几点：</p><ul><li>单例对面向对象的特性支持不友好</li><li>单例会隐藏类之间的依赖关系</li><li>单例对代码的扩展性不友好</li><li>单例对代码的可测试性不友好</li><li>单例不支持有参数的构造函数</li></ul><p>那有什么替代单例的解决方案呢？如果要完全解决这些问题，我们可能要从根上寻找其他方式来实现全局唯一类。比如，通过<strong>工厂模式</strong>、<strong>IOC 容器</strong>来保证全局唯一性。</p><p>有人把单例当作反模式，主张杜绝在项目中使用。个人觉得这有点极端。模式本身没有对错，关键看你怎么用。如果单例类并没有后续扩展的需求，并且不依赖外部系统，那设计成单例类就没有太大问题。对于一些全局类，在其他地方 new 的话，还要在类之间传来传去，不如直接做成单例类，使用起来简洁方便。</p><h3 id="工厂模式"><a href="#工厂模式" class="headerlink" title="工厂模式"></a>工厂模式</h3><p>工厂模式包括<strong>简单工厂</strong>、<strong>工厂方法</strong>、<strong>抽象工厂</strong>这 3 种细分模式。其中，简单工厂和工厂方法比较常用，抽象工厂的应用场景比较特殊，所以很少用到。</p><p>工厂模式用来创建不同但是相关类型的对象（继承同一父类或者接口的一组子类），由给定的参数来决定创建哪种类型的对象。实际上，如果创建对象的逻辑并不复杂，那我们直接通过 new 来创建对象就可以了，不需要使用工厂模式。当创建逻辑比较复杂，是一个“大工程”的时候，我们就考虑使用工厂模式，封装对象的创建过程，将对象的创建和使用相分离。当每个对象的创建逻辑都比较简单的时候，我推荐使用简单工厂模式，将多个对象的创建逻辑放到一个工厂类中。当每个对象的创建逻辑都比较复杂的时候，为了避免设计一个过于庞大的工厂类，我们推荐使用工厂方法模式，将创建逻辑拆分得更细，每个对象的创建逻辑独立到各自的工厂类中。</p><p>详细点说，工厂模式的作用有下面 4 个，这也是判断要不要使用工厂模式最本质的参考标准。</p><ul><li><em>封装变化</em>：创建逻辑有可能变化，封装成工厂类之后，创建逻辑的变更对调用者透明。</li><li><em>代码复用</em>：创建代码抽离到独立的工厂类之后可以复用。</li><li><em>隔离复杂性</em>：封装复杂的创建逻辑，调用者无需了解如何创建对象。</li><li><em>控制复杂度</em>：将创建代码抽离出来，让原本的函数或类职责更单一，代码更简洁。</li></ul><p>除此之外，我们还讲了工厂模式一个非常经典的应用场景：依赖注入框架</p><h3 id="建造者模式"><a href="#建造者模式" class="headerlink" title="建造者模式"></a>建造者模式</h3><p>建造者模式用来创建复杂对象，可以通过设置不同的可选参数，“定制化”地创建不同的对象。建造者模式的原理和实现比较简单，重点是掌握应用场景，避免过度使用。</p><ul><li>如果一个类中有很多属性，为了避免构造函数的参数列表过长，影响代码的可读性和易用性，我们可以通过构造函数配合 set() 方法来解决。但是，如果存在下面情况中的任意一种，我们就要考虑使用建造者模式了。</li><li>我们把类的必填属性放到构造函数中，强制创建对象的时候就设置。如果必填的属性有很多，把这些必填属性都放到构造函数中设置，那构造函数就又会出现参数列表很长的问题。如果我们把必填属性通过 set() 方法设置，那校验这些必填属性是否已经填写的逻辑就无处安放了。</li><li>如果类的属性之间有一定的依赖关系或者约束条件，我们继续使用构造函数配合 set() 方法的设计思路，那这些依赖关系或约束条件的校验逻辑就无处安放了。如果我们希望创建不可变对象，也就是说，对象在创建好之后，就不能再修改内部的属性值，要实现这个功能，我们就不能在类中暴露 set() 方法。构造函数配合 set() 方法来设置属性值的方式就不适用了。</li></ul><h3 id="原型模式"><a href="#原型模式" class="headerlink" title="原型模式"></a>原型模式</h3><p>如果对象的创建成本比较大，而同一个类的不同对象之间差别不大（大部分字段都相同），在这种情况下，我们可以利用对已有对象（原型）进行复制（或者叫拷贝）的方式，来创建新对象，以达到节省创建时间的目的。这种基于原型来创建对象的方式就叫作原型模式</p><p>原型模式有两种实现方法，深拷贝和浅拷贝。浅拷贝只会复制对象中基本数据类型数据和引用对象的内存地址，不会递归地复制引用对象，以及引用对象的引用对象……而深拷贝得到的是一份完完全全独立的对象。所以，深拷贝比起浅拷贝来说，更加耗时，更加耗内存空间。</p><p>如果要拷贝的对象是不可变对象，浅拷贝共享不可变对象是没问题的，但对于可变对象来说，浅拷贝得到的对象和原始对象会共享部分数据，就有可能出现数据被修改的风险，也就变得复杂多了。操作非常耗时的情况下，我们比较推荐使用浅拷贝，否则，没有充分的理由，不要为了一点点的性能提升而使用浅拷贝。</p><h2 id="结构型设计模式"><a href="#结构型设计模式" class="headerlink" title="结构型设计模式"></a>结构型设计模式</h2><p>结构型模式主要总结了一些类或对象组合在一起的经典结构，这些经典的结构可以解决特定应用场景的问题。结构型模式包括：<strong>代理模式</strong>、<strong>桥接模式</strong>、<strong>装饰器模式</strong>、<strong>适配器模式</strong>、<strong>门面模式</strong>、<strong>组合模式</strong>、<strong>享元模式</strong>。</p><h2 id="行为型设计模式"><a href="#行为型设计模式" class="headerlink" title="行为型设计模式"></a>行为型设计模式</h2><p>创建型设计模式主要解决“对象的创建”问题，结构型设计模式主要解决“类或对象的组合”问题，行为型设计模式主要解决的就是“类或对象之间的交互”问题。行为型模式比较多，有 11 种，它们分别是：<strong>观察者模式</strong>、<strong>模板模式</strong>、<strong>策略模式</strong>、<strong>职责链模式</strong>、<strong>迭代器模式</strong>、<strong>状态模式</strong>、<strong>访问者模式</strong>、<strong>备忘录模式</strong>、<strong>命令模式</strong>、<strong>解释器模式</strong>、<strong>中介模式</strong>。</p>]]></content>
    
    
    
    <tags>
      
      <tag>总结</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>个人Django网站集成QQ的第三方登录</title>
    <link href="/blog/posts/2205c935.html"/>
    <url>/blog/posts/2205c935.html</url>
    
    <content type="html"><![CDATA[<p><strong>体验地址</strong>: <a href="https://www.gishai.top/view/#/login">https://www.gishai.top/view/#/login</a></p><div class="note note-warning">特别注意的是,当前文章的Django版本是3.1.4</div><h2 id="使用social-auth-app-django"><a href="#使用social-auth-app-django" class="headerlink" title="使用social-auth-app-django"></a>使用social-auth-app-django</h2><figure class="highlight bat"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bat">pip install social-auth-app-django<br></code></pre></td></tr></table></figure><h2 id="settings中配置"><a href="#settings中配置" class="headerlink" title="settings中配置"></a>settings中配置</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs python">INSTALLED_APPS = (<br>    ...<br>    <span class="hljs-string">&#x27;social_django&#x27;</span>,<br>    ...<br>)<br><br>AUTHENTICATION_BACKENDS = (<br>    <span class="hljs-string">&#x27;social_core.backends.weibo.WeiboOAuth2&#x27;</span>,<br>    <span class="hljs-string">&#x27;social_core.backends.qq.QQOAuth2&#x27;</span>,<br>    <span class="hljs-string">&#x27;social_core.backends.weixin.WeixinOAuth2&#x27;</span>,<br>    <span class="hljs-string">&#x27;users.views.CustomBackend&#x27;</span>,<br>    <span class="hljs-string">&#x27;django.contrib.auth.backends.ModelBackend&#x27;</span>,<br>)<br><br>SOCIAL_AUTH_POSTGRES_JSONFIELD = <span class="hljs-literal">True</span> <span class="hljs-comment"># postgreSQL的配置</span><br>SOCIAL_AUTH_URL_NAMESPACE = <span class="hljs-string">&#x27;social&#x27;</span> <span class="hljs-comment"># 新增</span><br>SOCIAL_AUTH_QQ_KEY = <span class="hljs-string">&#x27;123456&#x27;</span><br>SOCIAL_AUTH_QQ_SECRET = <span class="hljs-string">&#x27;d34c96123456789&#x27;</span><br>SOCIAL_AUTH_QQ_USE_OPENID_AS_USERNAME = <span class="hljs-literal">True</span><br><span class="hljs-comment"># 登陆成功后的回调路由</span><br>SOCIAL_AUTH_LOGIN_REDIRECT_URL = <span class="hljs-string">&#x27;/&#x27;</span>  <span class="hljs-comment"># 登陆成功之后的路由</span><br>SOCIAL_AUTH_TRAILING_SLASH = <span class="hljs-literal">False</span><br><br>TEMPLATES = [<br>    &#123;<br>        ...<br>        <span class="hljs-string">&#x27;OPTIONS&#x27;</span>: &#123;<br>            ...<br>            <span class="hljs-string">&#x27;context_processors&#x27;</span>: [<br>                ...<br>                <span class="hljs-string">&#x27;social_django.context_processors.backends&#x27;</span>,<br>                <span class="hljs-string">&#x27;social_django.context_processors.login_redirect&#x27;</span>,<br>                ...<br>            ]<br>        &#125;<br>    &#125;<br>]<br></code></pre></td></tr></table></figure><h2 id="urls配置"><a href="#urls配置" class="headerlink" title="urls配置"></a>urls配置</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs python">url(<span class="hljs-string">&#x27;^accounts/&#x27;</span>, include(<span class="hljs-string">&#x27;social_django.urls&#x27;</span>, namespace=<span class="hljs-string">&#x27;social&#x27;</span>)),<br></code></pre></td></tr></table></figure><h2 id="QQ的配置"><a href="#QQ的配置" class="headerlink" title="QQ的配置"></a>QQ的配置</h2><p>qq比较特别,其余的按照网上说明配置即可成功,下面着重说明QQ的一些配置.</p><p><strong><a href="https://connect.qq.com/index.html">先申请QQ互联地址</a>-&gt;注册认证开发者-&gt;创建应用等待审核</strong></p><img src="/blog/posts/2205c935/image-20201215093905706.png" class="" title="image-20201215093905706"><h3 id="特别注意"><a href="#特别注意" class="headerlink" title="特别注意"></a>特别注意</h3><p><strong>填写回调的时候需要注意</strong></p><p>需要填写不带斜杠的地址,不然通过不了,说是<code>回调地址不合法：须为http或https开头的子目录。如http://qq.com/mycb</code></p><blockquote><p>特别感谢<a href="https://www.cnblogs.com/zmdComeOn/">子钦加油</a></p><p><a href="https://www.cnblogs.com/zmdComeOn/p/12667228.html">https://www.cnblogs.com/zmdComeOn/p/12667228.html</a></p><p><a href="https://blog.csdn.net/weixin_39944891/article/details/94739204">https://blog.csdn.net/weixin_39944891/article/details/94739204</a></p></blockquote>]]></content>
    
    
    
    <tags>
      
      <tag>Django</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>JavaScript与有限状态机</title>
    <link href="/blog/posts/5a12ed89.html"/>
    <url>/blog/posts/5a12ed89.html</url>
    
    <content type="html"><![CDATA[<h2 id="有限状态机"><a href="#有限状态机" class="headerlink" title="有限状态机"></a>有限状态机</h2><p>英语全称：finite-state machine，缩写：FSM。又称有限状态自动机（英语：finite-state automation，缩写：FSA），简称状态机，是表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型。</p><p>有限状态机是一个非常有用的模型，可以模拟世界上大部分事物。</p><img src="/blog/posts/5a12ed89/bg2013090201.png" class="" title="img"><p>简单说，它有三个特征：</p><ol><li>状态总数（state）是有限的。</li><li>任一时刻，只处在一种状态之中。</li><li>某种条件下，会从一种状态转变（transition）到另一种状态。</li></ol><p>它对JavaScript的意义在于，很多对象可以写成有限状态机。</p><p>举例来说，网页上有一个菜单元素。鼠标悬停的时候，菜单显示；鼠标移开的时候，菜单隐藏。如果使用有限状态机描述，就是这个菜单只有两种状态（显示和隐藏），鼠标会引发状态转变。</p><p>代码可以写成下面这样：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs javascript">　<span class="hljs-keyword">var</span> menu = &#123;<br>　　　　<span class="hljs-comment">// 当前状态</span><br>　　　　<span class="hljs-attr">currentState</span>: <span class="hljs-string">&#x27;hide&#x27;</span>,<br>　　<br>　　　　<span class="hljs-comment">// 绑定事件</span><br>　　　　<span class="hljs-attr">initialize</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;<br>　　　　　　<span class="hljs-keyword">var</span> self = <span class="hljs-built_in">this</span>;<br>　　　　　　self.on(<span class="hljs-string">&quot;hover&quot;</span>, self.transition);<br>　　　　&#125;,<br>　　<br>　　　　<span class="hljs-comment">// 状态转换</span><br>　　　　<span class="hljs-attr">transition</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>)</span>&#123;<br>　　　　　　<span class="hljs-keyword">switch</span>(<span class="hljs-built_in">this</span>.currentState) &#123;<br>　　　　　　　　<span class="hljs-keyword">case</span> <span class="hljs-string">&quot;hide&quot;</span>:<br>　　　　　　　　　　<span class="hljs-built_in">this</span>.currentState = <span class="hljs-string">&#x27;show&#x27;</span>;<br>　　　　　　　　　　doSomething();<br>　　　　　　　　　　<span class="hljs-keyword">break</span>;<br>　　　　　　　　<span class="hljs-keyword">case</span> <span class="hljs-string">&quot;show&quot;</span>:<br>　　　　　　　　　　<span class="hljs-built_in">this</span>.currentState = <span class="hljs-string">&#x27;hide&#x27;</span>;<br>　　　　　　　　　　doSomething();<br>　　　　　　　　　　<span class="hljs-keyword">break</span>;<br>　　　　　　　　<span class="hljs-keyword">default</span>:<br>　　　　　　　　　　<span class="hljs-built_in">console</span>.log(<span class="hljs-string">&#x27;Invalid State!&#x27;</span>);<br>　　　　　　　　　　<span class="hljs-keyword">break</span>;<br>　　　　　　&#125;<br>　　　　&#125;<br>　　<br>　　&#125;;<br></code></pre></td></tr></table></figure><p>可以看到，有限状态机的写法，逻辑清晰，表达力强，有利于封装事件。一个对象的状态越多、发生的事件越多，就越适合采用有限状态机的写法。</p><p>另外，JavaScript语言是一种<a href="http://www.ruanyifeng.com/blog/2012/12/asynchronous＿javascript.html">异步操作</a>特别多的语言，常用的解决方法是指定回调函数，但这样会造成代码结构混乱、难以测试和除错等问题。有限状态机提供了<a href="http://tech.pro/blog/1402/five-patterns-to-help-you-tame-asynchronous-javascript">更好的办法</a>：把异步操作与对象的状态改变挂钩，当异步操作结束的时候，发生相应的状态改变，由此再触发其他操作。这要比回调函数、事件监听、发布/订阅等解决方案，在逻辑上更合理，更易于降低代码的复杂度。</p><p>下面介绍一个有限状态机的函数库<a href="https://github.com/jakesgordon/javascript-state-machine">Javascript Finite State Machine</a>。这个库非常好懂，可以帮助我们加深理解，而且功能一点都不弱。</p><p>该库提供一个全局对象StateMachine，使用该对象的create方法，可以生成有限状态机的实例。</p><blockquote><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs javascript">　　<span class="hljs-keyword">var</span> fsm = StateMachine.create();<br></code></pre></td></tr></table></figure></blockquote><p>生成的时候，需要提供一个参数对象，用来描述实例的性质。比如，交通信号灯（红绿灯）可以这样描述：</p><blockquote><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs javascript">　　<span class="hljs-keyword">var</span> fsm = StateMachine.create(&#123;<br>　　<br>　　　　<span class="hljs-attr">initial</span>: <span class="hljs-string">&#x27;green&#x27;</span>,<br>　　<br>　　　　<span class="hljs-attr">events</span>: [<br>　　　　　　&#123; <span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;warn&#x27;</span>,  <span class="hljs-attr">from</span>: <span class="hljs-string">&#x27;green&#x27;</span>,  <span class="hljs-attr">to</span>: <span class="hljs-string">&#x27;yellow&#x27;</span> &#125;,<br>　　　　　　&#123; <span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;stop&#x27;</span>, <span class="hljs-attr">from</span>: <span class="hljs-string">&#x27;yellow&#x27;</span>, <span class="hljs-attr">to</span>: <span class="hljs-string">&#x27;red&#x27;</span> &#125;,<br>　　　　　　&#123; <span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;ready&#x27;</span>,  <span class="hljs-attr">from</span>: <span class="hljs-string">&#x27;red&#x27;</span>,    <span class="hljs-attr">to</span>: <span class="hljs-string">&#x27;yellow&#x27;</span> &#125;,<br>　　　　　　&#123; <span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;go&#x27;</span>, <span class="hljs-attr">from</span>: <span class="hljs-string">&#x27;yellow&#x27;</span>, <span class="hljs-attr">to</span>: <span class="hljs-string">&#x27;green&#x27;</span> &#125;<br>　　　　]<br>　　<br>　　&#125;);<br></code></pre></td></tr></table></figure></blockquote><p>交通信号灯的初始状态（initial）为green，events属性是触发状态改变的各种事件，比如warn事件使得green状态变成yellow状态，stop事件使得yellow状态变成red状态等等。</p><p>生成实例以后，就可以随时查询当前状态。</p><blockquote><ul><li>fsm.current ：返回当前状态。</li><li>fsm.is(s) ：返回一个布尔值，表示状态s是否为当前状态。</li><li>fsm.can(e) ：返回一个布尔值，表示事件e是否能在当前状态触发。</li><li>fsm.cannot(e) ：返回一个布尔值，表示事件e是否不能在当前状态触发。</li></ul></blockquote><p>Javascript Finite State Machine允许为每个事件指定两个回调函数，以warn事件为例：</p><blockquote><ul><li>onbefore<strong>warn</strong>：在warn事件发生之前触发。</li><li>onafter<strong>warn</strong>（可简写成onwarn） ：在warn事件发生之后触发。</li></ul></blockquote><p>同时，它也允许为每个状态指定两个回调函数，以green状态为例：</p><blockquote><ul><li>onleave<strong>green</strong> ：在离开green状态时触发。</li><li>onenter<strong>green</strong>（可简写成ongreen） ：在进入green状态时触发。</li></ul></blockquote><p>假定warn事件使得状态从green变为yellow，上面四类回调函数的发生顺序如下：onbefore<strong>warn</strong> → onleave<strong>green</strong> → onenter<strong>yellow</strong> → onafter<strong>warn</strong>。</p><p>除了为每个事件和状态单独指定回调函数，还可以为所有的事件和状态指定通用的回调函数。</p><blockquote><ul><li>onbeforeevent ：任一事件发生之前触发。</li><li>onleavestate ：离开任一状态时触发。</li><li>onenterstate ：进入任一状态时触发。</li><li>onafterevent ：任一事件结束后触发。</li></ul></blockquote><p>如果事件的回调函数里面有异步操作（比如与服务器进行Ajax通信），这时我们可能希望等到异步操作结束，再发生状态改变。这就要用到transition方法。</p><blockquote><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs javascript">　　fsm.onleavegreen = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>&#123;<br>　　　　light.fadeOut(<span class="hljs-string">&#x27;slow&#x27;</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;<br>　　　　　　fsm.transition();<br>　　　　&#125;);<br>　　　　<span class="hljs-keyword">return</span> StateMachine.ASYNC;<br>　　&#125;;<br>　　<br></code></pre></td></tr></table></figure></blockquote><p>上面代码的回调函数里面，有一个异步操作（light.fadeOut）。如果不希望状态立即改变，就要让回调函数返回StateMachine.ASYNC，表示状态暂时不改变；等到异步操作结束，再调用transition方法，使得状态发生改变。</p><p>Javascript Finite State Machine还允许指定错误处理函数，当发生了当前状态不可能发生的事件时自动触发。</p><blockquote><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs javascript">　　<span class="hljs-keyword">var</span> fsm = StateMachine.create(&#123;<br>　　　　<span class="hljs-comment">// ...</span><br>　　　　<span class="hljs-attr">error</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">eventName, <span class="hljs-keyword">from</span>, to, args, errorCode, errorMessage</span>) </span>&#123;<br>　　　　　　<span class="hljs-keyword">return</span> <span class="hljs-string">&#x27;event &#x27;</span> + eventName + <span class="hljs-string">&#x27;: &#x27;</span> + errorMessage;<br>　　　　&#125;,<br>　　　　<span class="hljs-comment">// ... </span><br>　　&#125;);<br></code></pre></td></tr></table></figure></blockquote><p>比如，当前状态是green，理论上这时只可能发生warn事件。要是这时发生了stop事件，就会触发上面的错误处理函数。</p><p>Javascript Finite State Machine的基本用法就是上面这些，更详细的介绍可以参见它的<a href="https://github.com/jakesgordon/javascript-state-machine">主页</a>。</p><blockquote><p>原文来自:<a href="http://www.ruanyifeng.com/blog/2013/09/finite-state_machine_for_javascript.html">http://www.ruanyifeng.com/blog/2013/09/finite-state_machine_for_javascript.html</a></p></blockquote>]]></content>
    
    
    
    <tags>
      
      <tag>JavaScript</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>mapbox的矢量切片工具:tippecanoe</title>
    <link href="/blog/posts/4ef7fe51.html"/>
    <url>/blog/posts/4ef7fe51.html</url>
    
    <content type="html"><![CDATA[<h1 id="矢量切片工具：tippecanoe"><a href="#矢量切片工具：tippecanoe" class="headerlink" title="矢量切片工具：tippecanoe"></a>矢量切片工具：tippecanoe</h1><p>Tippecanoe 用于将 GeoJSON, Geobuf, 或者 CSV 格式的矢量要素转换为<a href="https://www.mapbox.com/developers/vector-tiles/">矢量瓦片</a>。</p><h2 id="目的"><a href="#目的" class="headerlink" title="目的"></a>目的</h2><p>Tippecanoe 的目的是将数据制作为比例独立的视图，以使在任何缩放级别下，你都可以看到数据的密度和细节，而不是将数据简化或聚合。<br>如果你提供的是 OpenStreetMap 所有的数据，在小比例尺下，你应该看到类似于<a href="https://benfry.com/allstreets/map3.html">All Streets</a>的地图，而不是州际道路地图。<br>如果你提供的是洛杉矶的所有详细的建筑数据，并且将地图缩放到小比例尺下，绝大部分的单体建筑将不再可辨，但是你应该可以看到每个街区的范围和变化。<br>如果你提供的是一年内 twitter 推文的定位数据集，你应该可以发现所有兴趣点之间的关联和热门的旅游路线。</p><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><ul><li><p>OSX 操作系统使用 Homebrew 安装：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ brew install tippecanoe<br></code></pre></td></tr></table></figure></li><li><p>Ubuntu 系统最简单的方式是从源码中构建：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ git <span class="hljs-built_in">clone</span> git@github.com:mapbox/tippecanoe.git<br>$ <span class="hljs-built_in">cd</span> tippecanoe<br>$ make -j<br>$ make install<br></code></pre></td></tr></table></figure></li><li><p>Window系统的最简单的方式是安装一个Ubuntu系统：</p><p>在windows10 第3个稳定版发布以后，支持内嵌的linux系统，下面我们一起来看看，怎么使用它内部自带的linux系统。</p><p><a href="https://jingyan.baidu.com/article/c85b7a64a56c7f003aac954f.html">https://jingyan.baidu.com/article/c85b7a64a56c7f003aac954f.html</a></p></li></ul><p>如果编译中出现问题，可能是你的C++编译器需要升级，或者缺少必要的依赖包，详细请查看<a href="https://github.com/cgcs2000/tippecanoe#development">文档</a>。</p><h2 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ tippecanoe -o file.mbtiles [options] [file.json file.json.gz file.geobuf ...]<br></code></pre></td></tr></table></figure><p>如果没有指定文件，会从默认路径读取 GeoJSON 文件；如果指定了多个文件，每一个文件将会被当做一个图层。<br>GeoJSON 要素不一定非得包含在 FeatureCollection 中。你可以将多个 GeoJSON 要素或者文件合并。</p><h2 id="Try-this-first"><a href="#Try-this-first" class="headerlink" title="Try this first"></a>Try this first</h2><p>如果你不确定使用什么选项，请尝试一下命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ tippecanoe -o out.mbtiles -zg --drop-densest-as-needed in.geojson<br></code></pre></td></tr></table></figure><p>使用<code>-zg</code> 选项，Tippecanoe 将自动选择一个可以反映原始数据精度的最大级别（如果结果没有达到你想要的效果，你也可以使用<code>-z</code> 选项手动设置最大级别）。<br>如果生产出的切片太大，可以使用 <code>--drop-densest-as-needed</code>选项，来让Tippecanoe自动删除各个级别下最不可见的要素。（如果它删除了太多的要素，你可以使用<code>-x</code>选项来删除不必要的属性字段）</p><h2 id="选项"><a href="#选项" class="headerlink" title="选项"></a>选项</h2><p>tippecanoe 切片有很多选项，但是大部分情况下你并不想要使用它们，除了使用 <code>-o output.mbtiles</code><br>来定义输出瓦片文件名，或者再加上<code>-f</code> 选项来强制删除同名文件。</p><p>如果你不确定需要切片的最大级别，<code>-zg</code> 选项可以根据源数据自动计算出一个最大级别。</p><p>通常，在最大切片级别以下的级别，tippecanoe 会舍弃部分点要素，以防止瓦片过大。如果你的数据集本来就不大，你想要保留所有要素，可以使用<code>-r1</code>选项。如果你确实是想要简化数据，但是又不想简化得过于稀疏，可以使用 <code>-B</code> 选项设置一个小于最大级别的数值。</p><p>通过以上设置，如果你的切片仍然很大，你可以使用 <code>--drop-densest-as-needed</code> 选项来进一步简化要素。</p><p>如果你的要素包含很多属性，你可以使用<code>-y</code>选项来选择只保留你给定的字段。</p><p>如果你的GeoJSON 文件是格式化后的，使用<code>-p</code>可以加快文件读取。</p><h3 id="输出选项"><a href="#输出选项" class="headerlink" title="输出选项"></a>输出选项</h3><ul><li><code>-o file.mbtiles</code> 或 <code>--output=file.mbtiles</code>：指定输出文件名</li><li><code>-e directory</code> 或 <code>--output-to-directory=directory</code>：指定输出文件路径</li><li><code>-f</code> 或 <code>--force</code>：若存在同名文件则删除</li><li><code>-F</code> 或 <code>--allow-existing</code></li></ul><h3 id="瓦片集属性选项"><a href="#瓦片集属性选项" class="headerlink" title="瓦片集属性选项"></a>瓦片集属性选项</h3><ul><li><code>-n name</code> 或 <code>--name=name</code>: 给瓦片集设置一个易读的名字</li><li><code>-A text</code> 或 <code>--attribution=text</code>： 瓦片集</li><li><code>-N description</code> 或 <code>--description=description</code>: 瓦片集描述</li></ul><h3 id="输入文件和图层名"><a href="#输入文件和图层名" class="headerlink" title="输入文件和图层名"></a>输入文件和图层名</h3><ul><li><p><code>name.json</code> 或 <code>name.geojson</code>：读取 GeoJSON 文件</p></li><li><p><code>name.json.gz</code> 或 <code>name.geojson.gz</code>：读取 GeoJSON 压缩文件</p></li><li><p><code>name.geobuf</code>：读取 Geobuf 文件</p></li><li><p><code>name.csv</code>：读取 CSV 文件</p></li><li><p><code>-l name</code> 或 <code>--layer=name</code>: 使用自定义图层名，而不是默认的输入文件名作为图层名，如果有多个输入文件，将合并为一个图层，除非使用<code>-L</code>选项来分别指定图层名。</p></li><li><p><code>-L name:file.json</code> 或 <code>--named-layer=name:file.json</code>：定义每个文件的对应的图层名</p></li><li><p><code>-L&#123;layer-json&#125;</code>或 <code>--named-layer=&#123;layer-json&#125;</code>: 通过 json 对象定义图层。示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">tippecanoe -z5 -o world.mbtiles -L<span class="hljs-string">&#x27;&#123;&quot;file&quot;:&quot;ne_10m_admin_0_countries.json&quot;, &quot;layer&quot;:&quot;countries&quot;, &quot;description&quot;:&quot;Natural Earth countries&quot;&#125;&#x27;</span><br></code></pre></td></tr></table></figure></li></ul><h3 id="坐标系"><a href="#坐标系" class="headerlink" title="坐标系"></a>坐标系</h3><ul><li><code>-s projection</code> 或 <code>--projection=projection</code>: 给定输入文件的坐标系统。当前支持的坐标系有<code>EPSG:4326</code>（WGS84，默认值）、<code>EPSG:3857</code>（Web Mercator）。请尽量使用 WGS84 坐标系统的数据集。</li></ul><h3 id="切片级别"><a href="#切片级别" class="headerlink" title="切片级别"></a>切片级别</h3><ul><li><code>-z zoom</code> 或 <code>--maximum-zoom=zoom</code>:切片的最大级别（默认为14）</li><li><code>-zg</code> 或 <code>--maximum-zoom=g</code>: 根据数据的密集程度自动计算一个最大级别</li><li><code>-Z zoom</code> 或 <code>--minimum-zoom=zoom</code>: 切片的最小级别（默认0）</li><li><code>-ae</code> 或 <code>--extend-zooms-if-still-dropping</code>: 如果在大级别下瓦片仍然很大，它将自动增加最大级别，以使最大级别下没有要素被删除</li><li><code>-R zoom/x/y</code> 或 <code>--one-tile=zoom/x/y</code>:</li></ul><p>如果你知道你想要的切片的数据精度，那么你就可以根据以下表格来设置切片级别：</p><div class="table-container"><table><thead><tr><th style="text-align:left">级别</th><th style="text-align:left">精度 (英尺)</th><th style="text-align:left">精度 (m)</th></tr></thead><tbody><tr><td style="text-align:left"><code>-z0</code></td><td style="text-align:left">32000 ft</td><td style="text-align:left">10000 m</td></tr><tr><td style="text-align:left"><code>-z1</code></td><td style="text-align:left">16000 ft</td><td style="text-align:left">5000 m</td></tr><tr><td style="text-align:left"><code>-z2</code></td><td style="text-align:left">8000 ft</td><td style="text-align:left">2500 m</td></tr><tr><td style="text-align:left"><code>-z3</code></td><td style="text-align:left">4000 ft</td><td style="text-align:left">1250 m</td></tr><tr><td style="text-align:left"><code>-z4</code></td><td style="text-align:left">2000 ft</td><td style="text-align:left">600 m</td></tr><tr><td style="text-align:left"><code>-z5</code></td><td style="text-align:left">1000 ft</td><td style="text-align:left">300 m</td></tr><tr><td style="text-align:left"><code>-z6</code></td><td style="text-align:left">500 ft</td><td style="text-align:left">150 m</td></tr><tr><td style="text-align:left"><code>-z7</code></td><td style="text-align:left">250 ft</td><td style="text-align:left">80 m</td></tr><tr><td style="text-align:left"><code>-z8</code></td><td style="text-align:left">125 ft</td><td style="text-align:left">40 m</td></tr><tr><td style="text-align:left"><code>-z9</code></td><td style="text-align:left">64 ft</td><td style="text-align:left">20 m</td></tr><tr><td style="text-align:left"><code>-z10</code></td><td style="text-align:left">32 ft</td><td style="text-align:left">10 m</td></tr><tr><td style="text-align:left"><code>-z11</code></td><td style="text-align:left">16 ft</td><td style="text-align:left">5 m</td></tr><tr><td style="text-align:left"><code>-z12</code></td><td style="text-align:left">8 ft</td><td style="text-align:left">2 m</td></tr><tr><td style="text-align:left"><code>-z13</code></td><td style="text-align:left">4 ft</td><td style="text-align:left">1 m</td></tr><tr><td style="text-align:left"><code>-z14</code></td><td style="text-align:left">2 ft</td><td style="text-align:left">0.5 m</td></tr><tr><td style="text-align:left"><code>-z15</code></td><td style="text-align:left">1 ft</td><td style="text-align:left">0.25 m</td></tr></tbody></table></div><h3 id="属性筛选"><a href="#属性筛选" class="headerlink" title="属性筛选"></a>属性筛选</h3><ul><li><code>-x name</code> 或 <code>--exclude=name</code>： 指定切片中应剔除的字段。</li><li><code>-y name</code> 或 <code>--include=name</code>： 指定切片中应包含的字段。</li></ul><h2 id="Cookbook"><a href="#Cookbook" class="headerlink" title="Cookbook"></a>Cookbook</h2><h3 id="线要素（全球铁路），在所有级别可见"><a href="#线要素（全球铁路），在所有级别可见" class="headerlink" title="线要素（全球铁路），在所有级别可见"></a>线要素（全球铁路），在所有级别可见</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash">curl -L -O https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_railroads.zip<br>unzip ne_10m_railroads.zip<br>ogr2ogr -f GeoJSON ne_10m_railroads.geojson ne_10m_railroads.shp<br><br>tippecanoe -zg -o ne_10m_railroads.mbtiles --drop-densest-as-needed --extend-zooms-if-still-dropping ne_10m_railroads.geojson<br></code></pre></td></tr></table></figure><ul><li><code>-zg</code>: 自动选择最大级别；</li><li><code>--drop-densest-as-needed</code>: 如果在小级别下瓦片太大，该选项将自动简化要素；</li><li><code>--extend-zooms-if-still-dropping</code>: 如果在大级别下瓦片仍然很大，它将自动增加最大级别，以使最大级别下没有要素被删除；</li></ul><h3 id="不连续的面要素（美国罗德岛），在所有级别可见"><a href="#不连续的面要素（美国罗德岛），在所有级别可见" class="headerlink" title="不连续的面要素（美国罗德岛），在所有级别可见"></a>不连续的面要素（美国罗德岛），在所有级别可见</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash">curl -L -O https://usbuildingdata.blob.core.windows.net/usbuildings-v1-1/RhodeIsland.zip<br>unzip RhodeIsland.zip<br><br>tippecanoe -zg -o RhodeIsland.mbtiles --drop-densest-as-needed --extend-zooms-if-still-dropping RhodeIsland.geojson<br></code></pre></td></tr></table></figure><ul><li><code>-zg</code>: 自动选择最大级别；</li><li><code>--drop-densest-as-needed</code>: 如果在小级别下瓦片太大，该选项将自动简化要素；</li><li><code>--extend-zooms-if-still-dropping</code>: 如果在大级别下瓦片仍然很大，它将自动增加最大级别，以使最大级别下没有要素被删除；</li></ul><h3 id="连续的面要素（行政区划），在所有级别可见"><a href="#连续的面要素（行政区划），在所有级别可见" class="headerlink" title="连续的面要素（行政区划），在所有级别可见"></a>连续的面要素（行政区划），在所有级别可见</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash">curl -L -O https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_admin_1_states_provinces.zip<br>unzip -o ne_10m_admin_1_states_provinces.zip<br>ogr2ogr -f GeoJSON ne_10m_admin_1_states_provinces.geojson ne_10m_admin_1_states_provinces.shp<br><br>tippecanoe -zg -o ne_10m_admin_1_states_provinces.mbtiles --coalesce-densest-as-needed --extend-zooms-if-still-dropping ne_10m_admin_1_states_provinces.geojson<br></code></pre></td></tr></table></figure><ul><li><code>-zg</code>: 自动选择最大级别；</li><li><code>--extend-zooms-if-still-dropping</code>: 如果在大级别下瓦片仍然很大，它将自动增加最大级别，以使最大级别下没有要素被删除；</li><li><code>--coalesce-densest-as-needed</code>: 如果瓦片在低级别或中等级别下比较大，该选项将合并要素；</li></ul><h3 id="海量点数据（公交车GPS轨迹数据），可视化，在所有级别可见"><a href="#海量点数据（公交车GPS轨迹数据），可视化，在所有级别可见" class="headerlink" title="海量点数据（公交车GPS轨迹数据），可视化，在所有级别可见"></a>海量点数据（公交车GPS轨迹数据），可视化，在所有级别可见</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">curl -L -O ftp://avl-data.sfmta.com/avl_data/avl_raw/sfmtaAVLRawData01012013.csv<br>sed <span class="hljs-string">&#x27;s/PREDICTABLE.*/PREDICTABLE/&#x27;</span> sfmtaAVLRawData01012013.csv &gt; sfmta.csv<br>tippecanoe -zg -o sfmta.mbtiles --drop-densest-as-needed --extend-zooms-if-still-dropping sfmta.csv<br></code></pre></td></tr></table></figure><p>(<code>sed</code> 命令用于清除不必要的字段)</p><ul><li><code>-zg</code>: 自动选择最大级别；</li><li><code>--drop-densest-as-needed</code>: 如果在小级别下瓦片太大，该选项将自动简化要素；</li><li><code>--extend-zooms-if-still-dropping</code>: 如果在大级别下瓦片仍然很大，它将自动增加最大级别，以使最大级别下没有要素被删除；</li></ul><h3 id="低级别显示国家边界，高级别显示州边界"><a href="#低级别显示国家边界，高级别显示州边界" class="headerlink" title="低级别显示国家边界，高级别显示州边界"></a>低级别显示国家边界，高级别显示州边界</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs bash">curl -L -O https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_admin_0_countries.zip<br>unzip ne_10m_admin_0_countries.zip<br>ogr2ogr -f GeoJSON ne_10m_admin_0_countries.geojson ne_10m_admin_0_countries.shp<br><br>curl -L -O https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_admin_1_states_provinces.zip<br>unzip -o ne_10m_admin_1_states_provinces.zip<br>ogr2ogr -f GeoJSON ne_10m_admin_1_states_provinces.geojson ne_10m_admin_1_states_provinces.shp<br><br>tippecanoe -z3 -o countries-z3.mbtiles --coalesce-densest-as-needed ne_10m_admin_0_countries.geojson<br>tippecanoe -zg -Z4 -o states-Z4.mbtiles --coalesce-densest-as-needed --extend-zooms-if-still-dropping ne_10m_admin_1_states_provinces.geojson<br>tile-join -o states-countries.mbtiles countries-z3.mbtiles states-Z4.mbtiles<br></code></pre></td></tr></table></figure><ul><li>Countries:<ul><li><code>-z3</code>: 最大切片级别为3，即只切 0 - 3 级别的瓦片；</li><li><code>--coalesce-densest-as-needed</code>: 如果瓦片在低级别或中等级别下比较大，该选项将合并要素；</li></ul></li><li>States and Provinces:<ul><li><code>-Z4</code>: 最小切片级别为4；</li><li><code>-zg</code>: 自动选择最大级别；</li><li><code>--coalesce-densest-as-needed</code>: 如果瓦片在低级别或中等级别下比较大，该选项将合并要素；</li><li><code>--extend-zooms-if-still-dropping</code>: 如果在大级别下瓦片仍然很大，它将自动增加最大级别，以使最大级别下没有要素被删除；</li></ul></li></ul><h3 id="多个数据源切片为独立的图层"><a href="#多个数据源切片为独立的图层" class="headerlink" title="多个数据源切片为独立的图层"></a>多个数据源切片为独立的图层</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs bash">curl -L -O https://www2.census.gov/geo/tiger/TIGER2010/COUNTY/2010/tl_2010_17_county10.zip<br>unzip tl_2010_17_county10.zip<br>ogr2ogr -f GeoJSON tl_2010_17_county10.geojson tl_2010_17_county10.shp<br><br>curl -L -O https://www2.census.gov/geo/tiger/TIGER2010/COUNTY/2010/tl_2010_18_county10.zip<br>unzip tl_2010_18_county10.zip<br>ogr2ogr -f GeoJSON tl_2010_18_county10.geojson tl_2010_18_county10.shp<br><br>tippecanoe -zg -o counties-separate.mbtiles --coalesce-densest-as-needed --extend-zooms-if-still-dropping tl_2010_17_county10.geojson tl_2010_18_county10.geojson<br></code></pre></td></tr></table></figure><ul><li><code>-zg</code>: 自动选择最大级别；</li><li><code>--coalesce-densest-as-needed</code>: 如果瓦片在低级别或中等级别下比较大，该选项将合并要素；</li><li><code>--extend-zooms-if-still-dropping</code>: 如果在大级别下瓦片仍然很大，它将自动增加最大级别，以使最大级别下没有要素被删除；</li></ul><h3 id="多个数据源切片并合并为一个图层"><a href="#多个数据源切片并合并为一个图层" class="headerlink" title="多个数据源切片并合并为一个图层"></a>多个数据源切片并合并为一个图层</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs bash">curl -L -O https://www2.census.gov/geo/tiger/TIGER2010/COUNTY/2010/tl_2010_17_county10.zip<br>unzip tl_2010_17_county10.zip<br>ogr2ogr -f GeoJSON tl_2010_17_county10.geojson tl_2010_17_county10.shp<br><br>curl -L -O https://www2.census.gov/geo/tiger/TIGER2010/COUNTY/2010/tl_2010_18_county10.zip<br>unzip tl_2010_18_county10.zip<br>ogr2ogr -f GeoJSON tl_2010_18_county10.geojson tl_2010_18_county10.shp<br><br>tippecanoe -zg -o counties-merged.mbtiles -l counties --coalesce-densest-as-needed --extend-zooms-if-still-dropping tl_2010_17_county10.geojson tl_2010_18_county10.geojson<br></code></pre></td></tr></table></figure><ul><li><code>-l counties</code>: 图层名默认为文件名，也可以使用该选项自定义；</li></ul><h2 id="tile-join"><a href="#tile-join" class="headerlink" title="tile-join"></a>tile-join</h2><p>用于合并或复制矢量瓦片。</p><h2 id="tippecanoe-decode"><a href="#tippecanoe-decode" class="headerlink" title="tippecanoe-decode"></a>tippecanoe-decode</h2><p>用于将矢量瓦片逆向转换为 GeoJSON。</p><h2 id="tippecanoe-enumerate"><a href="#tippecanoe-enumerate" class="headerlink" title="tippecanoe-enumerate"></a>tippecanoe-enumerate</h2><p>用于列举mbtiles中的矢量瓦片。</p>]]></content>
    
    
    <categories>
      
      <category>mapbox</category>
      
    </categories>
    
    
    <tags>
      
      <tag>Mapbox</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>着色器之书16</title>
    <link href="/blog/posts/c5427cd6.html"/>
    <url>/blog/posts/c5427cd6.html</url>
    
    <content type="html"><![CDATA[<h2 id="影像操作"><a href="#影像操作" class="headerlink" title="影像操作"></a>影像操作</h2><h3 id="反色—-颠倒黑白"><a href="#反色—-颠倒黑白" class="headerlink" title="反色—-颠倒黑白"></a>反色—-颠倒黑白</h3><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/16/inv.frag" data-textures="/blog/images/00.jpg,/blog/images/01.jpg"    style="width:100%;height:auto;margin-bottom:10px"></div></div><h3 id="叠加-相减-乘积性变换和其他"><a href="#叠加-相减-乘积性变换和其他" class="headerlink" title="叠加,相减,乘积性变换和其他"></a>叠加,相减,乘积性变换和其他</h3><img src="/blog/posts/c5427cd6/02.jpg" class=""><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/16/operations.frag" data-textures="/blog/images/00.jpg,/blog/images/01.jpg"    style="width:100%;height:auto;margin-bottom:10px"></div></div><h3 id="PS混合模式"><a href="#PS混合模式" class="headerlink" title="PS混合模式"></a>PS混合模式</h3><img src="/blog/posts/c5427cd6/03.jpg" class=""><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/16/blend.frag" data-textures="/blog/images/04.jpg,/blog/images/05.jpg"    style="width:100%;height:auto;margin-bottom:10px"></div></div>]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
    <tags>
      
      <tag>着色器之书</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>着色器之书15</title>
    <link href="/blog/posts/5c4b2d6c.html"/>
    <url>/blog/posts/5c4b2d6c.html</url>
    
    <content type="html"><![CDATA[<h1 id="图像处理"><a href="#图像处理" class="headerlink" title="图像处理"></a>图像处理</h1><h2 id="贴图"><a href="#贴图" class="headerlink" title="贴图"></a>贴图</h2><img src="/blog/posts/5c4b2d6c/01.jpg" class=""><p>Graphic cards (GPUs) have special memory types for images. Usually on CPUs images are stored as arrays of bytes but GPUs store images as <code>sampler2D</code> which is more like a table (or matrix) of floating point vectors. More interestingly, the values of this <em>table</em> of vectors are continuous. That means that values between pixels are interpolated in a low level.</p><p>In order to use this feature we first need to <em>upload</em> the image from the CPU to the GPU, to then pass the <code>id</code> of the texture to the right <a href="../05"><code>uniform</code></a>. All that happens outside the shader.</p><p>Once the texture is loaded and linked to a valid <code>uniform sampler2D</code> you can ask for specific color value at specific coordinates (formated on a <a href="index.html#vec2.md"><code>vec2</code></a> variable) using the <a href="index.html#texture2D.md"><code>texture2D()</code></a> function which will return a color formatted on a <a href="index.html#vec4.md"><code>vec4</code></a> variable.</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">vec4</span> <span class="hljs-built_in">texture2D</span>(<span class="hljs-type">sampler2D</span> <span class="hljs-built_in">texture</span>, <span class="hljs-type">vec2</span> coordinates)  <br></code></pre></td></tr></table></figure><p>Check the following code where we load Hokusai’s Wave (1830) as <code>uniform sampler2D u_tex0</code> and we call every pixel of it inside the billboard:</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/15/texture.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>If you pay attention you will note that the coordinates for the texture are normalized! What a surprise right? Textures coordinates are consistent with the rest of the things we had seen and their coordinates are between 0.0 and 1.0 which match perfectly with the normalized space coordinates we have been using.</p><p>Now that you have seen how we correctly load a texture, it is time to experiment to discover what we can do with it, by trying:</p><ul><li>Scaling the previous texture by half.</li><li>Rotating the previous texture 90 degrees.</li><li>Hooking the mouse position to the coordinates to move it.</li></ul><p>Why you should be excited about textures? Well first of all forget about the sad 255 values for channel; once your image is transformed into a <code>uniform sampler2D</code> you have all the values between 0.0 and 1.0 (depending on what you set the <code>precision</code> to ). That’s why shaders can make really beautiful post-processing effects.</p><p>Second, the <a href="index.html#vec2.md"><code>vec2()</code></a> means you can get values even between pixels. As we said before the textures are a continuum. This means that if you set up your texture correctly you can ask for values all around the surface of your image and the values will smoothly vary from pixel to pixel with no jumps!</p><p>Finally, you can set up your image to repeat in the edges, so if you give values over or lower of the normalized 0.0 and 1.0, the values will wrap around starting over.</p><p>All these features make your images more like an infinite spandex fabric. You can stretch and shrink your texture without noticing the grid of bytes they are originally composed of or the ends of it. To experience this take a look at the following code where we distort a texture using <a href="../11/">the noise function we already made</a>.</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/15/texture-noise.frag" data-textures="/blog/images/hokusai.jpg"     style="width:100%;height:auto;margin-bottom:10px"></div></div><h2 id="纹理分辨率"><a href="#纹理分辨率" class="headerlink" title="纹理分辨率"></a>纹理分辨率</h2><p>Above examples play well with squared images, where both sides are equal and match our squared billboard. But for non-squared images things can be a little more tricky, and unfortunately centuries of pictorial art and photography found more pleasant to the eye non-squared proportions for images.</p><img src="/blog/posts/5c4b2d6c/nicephore.jpg" class="" title="Joseph Nicéphore Niépce (..&#x2F;..&#x2F;..&#x2F;..&#x2F;Github&#x2F;thebookofshaders&#x2F;15&#x2F;nicephore.jpg)"><p>How we can solve this problem? Well we need to know the original proportions of the image to know how to stretch the texture correctly in order to have the original <a href="http://en.wikipedia.org/wiki/Aspect_ratio"><em>aspect ratio</em></a>. For that the texture width and height are passed to the shader as an <code>uniform</code>, which in our example framework are passed as an <code>uniform vec2</code> with the same name of the texture followed with proposition <code>Resolution</code>. Once we have this information on the shader we can get the aspect ratio by dividing the <code>width</code> for the <code>height</code> of the texture resolution. Finally by multiplying this ratio to the coordinates on <code>y</code> we will shrink this axis to match the original proportions.</p><p>Uncomment line 21 of the following code to see this in action.</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/15/texture-resolution.frag" data-textures="/blog/images/nicephore.jpg"     style="width:100%;height:auto;margin-bottom:10px"></div></div><ul><li>What we need to do to center this image?</li></ul><h2 id="数码装饰"><a href="#数码装饰" class="headerlink" title="数码装饰"></a>数码装饰</h2><img src="/blog/posts/5c4b2d6c/03.jpg" class=""><p>You may be thinking that this is unnecessarily complicated… and you are probably right. Also this way of working with images leaves enough room to different hacks and creative tricks. Try to imagine that you are an upholster and by stretching and folding a fabric over a structure you can create better and new patterns and techniques.</p><img src="/blog/posts/5c4b2d6c/muybridge.jpg" class="" title="Eadweard" alt="s Muybridge study of motion"><p>This level of craftsmanship links back to some of the first optical experiments ever made. For example on games <em>sprite animations</em> are very common, and is inevitably to see on it reminiscence to phenakistoscope, zoetrope and praxinoscope.</p><p>This could seem simple but the possibilities of modifying textures coordinates are enormous. For example:</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/15/texture-sprite.frag" data-textures="/blog/images/muybridge.jpg"     style="width:100%;height:auto;margin-bottom:10px"></div></div><p>Now is your turn:</p><ul><li><p>Can you make a kaleidoscope using what we have learned?</p></li><li><p>Way before Oculus or google cardboard, stereoscopic photography was a big thing. Could you code a simple shader to re-use these beautiful images?</p></li></ul><div class="container" style="margin:0;padding:0">   <a href="../edit.php#10/ikeda-03.frag"><canvas id="custom" class="canvas" data-fragment-url="ikeda-03.frag"   style="width:100%;height:auto;margin-bottom:10px"></canvas></a></div><ul><li>What other optical toys can you re-create using textures?</li></ul><p>In the next chapters we will learn how to do some image processing using shaders. You will note that finally the complexity of shader makes sense, because it was in a big sense designed to do this type of process. We will start doing some image operations!</p>]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
    <tags>
      
      <tag>着色器之书</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>着色器之书14</title>
    <link href="/blog/posts/2b4c1dfa.html"/>
    <url>/blog/posts/2b4c1dfa.html</url>
    
    <content type="html"><![CDATA[<h2 id="分形的例子"><a href="#分形的例子" class="headerlink" title="分形的例子"></a>分形的例子</h2><p><a href="https://www.shadertoy.com/view/lsX3W4">https://www.shadertoy.com/view/lsX3W4</a></p><p><a href="https://www.shadertoy.com/view/Mss3Wf">https://www.shadertoy.com/view/Mss3Wf</a></p><p><a href="https://www.shadertoy.com/view/4df3Rn">https://www.shadertoy.com/view/4df3Rn</a></p><p><a href="https://www.shadertoy.com/view/Mss3R8">https://www.shadertoy.com/view/Mss3R8</a></p><p><a href="https://www.shadertoy.com/view/4dfGRn">https://www.shadertoy.com/view/4dfGRn</a></p><p><a href="https://www.shadertoy.com/view/lss3zs">https://www.shadertoy.com/view/lss3zs</a></p><p><a href="https://www.shadertoy.com/view/4dXGDX">https://www.shadertoy.com/view/4dXGDX</a></p><p><a href="https://www.shadertoy.com/view/XsXGz2">https://www.shadertoy.com/view/XsXGz2</a></p><p><a href="https://www.shadertoy.com/view/lls3D7">https://www.shadertoy.com/view/lls3D7</a></p><p><a href="https://www.shadertoy.com/view/XdB3DD">https://www.shadertoy.com/view/XdB3DD</a></p><p><a href="https://www.shadertoy.com/view/XdBSWw">https://www.shadertoy.com/view/XdBSWw</a></p><p><a href="https://www.shadertoy.com/view/llfGD2">https://www.shadertoy.com/view/llfGD2</a></p><p><a href="https://www.shadertoy.com/view/Mlf3RX">https://www.shadertoy.com/view/Mlf3RX</a></p>]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
    <tags>
      
      <tag>着色器之书</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>着色器之书13</title>
    <link href="/blog/posts/b5288859.html"/>
    <url>/blog/posts/b5288859.html</url>
    
    <content type="html"><![CDATA[<img src="/blog/posts/b5288859/rangel.jpg" class="" title="Due East over Shadequarter Mountain - Matthew Rangel (..&#x2F;..&#x2F;..&#x2F;..&#x2F;Github&#x2F;thebookofshaders&#x2F;13&#x2F;rangel.jpg)"><h2 id="分形布朗运动（Fractal-Brownian-Motion）"><a href="#分形布朗运动（Fractal-Brownian-Motion）" class="headerlink" title="分形布朗运动（Fractal Brownian Motion）"></a>分形布朗运动（Fractal Brownian Motion）</h2><p>噪声对不同的人来说有不同的意义。音乐家把它当成一种令人不安的声响，通信工程师把它当作干扰信号，天体物理学家把它看作宇宙微波背景辐射。这些概念吸引着我们去探索处处可见的随机性的物理原因。但是，让我们从更基础，也更简单的开始：波和波的属性。波就是某些属性随着时间波动变化。声波是气压的波动，电磁波是电场和磁场的波动。波的两个重要特征是振幅（amplitude）和频率（frequency）。一个简单的线性波（一维）的方程如下：</p><div class="simpleFunction" data="float amplitude = 1.;float frequency = 1.;y = amplitude * sin(x * frequency);"></div><ul><li>试着改变频率和振幅的值，理解他们如何影响波形。</li><li>使用造型函数，试着随时间改变振幅。</li><li>使用造型函数，试着随时间改变频率。</li></ul><p>通过做后两个练习，你已经知道怎么“调节”一个正弦波。恭喜你，你刚刚创造了一个 AM（调幅）和 FM（调频）波！</p><p>波的另一个有趣的特性是可以相加，这一特性的正式说法叫叠加性。调一调下面几行代码，注意我们加上那些不同振幅和频率的正弦波的时候，总的波形是如何变化的。</p><div class="simpleFunction" data="float amplitude = 1.;float frequency = 1.;y = sin(x * frequency);float t = 0.01*(-u_time*130.0);y += sin(x*frequency*2.1 + t)*4.5;y += sin(x*frequency*1.72 + t*1.121)*4.0;y += sin(x*frequency*2.221 + t*0.437)*5.0;y += sin(x*frequency*3.1122+ t*4.269)*2.5;y *= amplitude*0.06;"></div><ul><li>试试改变加上去的波的振幅和频率。</li><li>有没有可能两个波正好相互抵消？如果是的话，会是什么样子？</li><li>有没有一种叠加波的方法，让他们互相放大？</li></ul><p>从音乐理论上说，每个音符都和一个特定的频率相关联。这些音符和频率的关系遵从一定的模式，也就是我们所说的音阶，一个八度（octave）对应着频率上的加倍或减半。</p><p>现在，让我们把正弦波放在一边，想想 Perlin 噪声！Perlin 噪声的基本形式看起来和正弦波有点相似。它的振幅和频率有着某种变化，但振幅保持着合理的连续性，而且频率被限制在一个距离中心频率很小的范围内。尽管它不像正弦波那样规则，并且把几个不同缩放比例的 Perlin 噪声相加更容易制造出随机形态。把一些正弦波相加也是有可能制造随机形态的，但那需要很多不同的波叠加才能把他们的天生的周期性和规则性隐藏起来。</p><p>通过在循环（循环次数为 <em>octaves</em>，一次循环为一个八度）中叠加噪声，并以一定的倍数（<em>lacunarity</em>，间隙度）连续升高频率，同时以一定的比例（<em>gain</em>，增益）降低 <strong>噪声</strong> 的振幅，最终的结果会有更好的细节。这项技术叫“分形布朗运动（fractal Brownian Motion）”（<em>fBM</em>），或者“分形噪声（fractal noise）”，最简单的实现如下：</p><div class="simpleFunction" data="// Propertiesconst int octaves = 1;float lacunarity = 2.0;float gain = 0.5;//// Initial valuesfloat amplitude = 0.5;float frequency = 1.;//// Loop of octavesfor (int i = 0; i < octaves; i++) {&#9;y += amplitude * noise(frequency*x);&#9;frequency *= lacunarity;&#9;amplitude *= gain;}"></div><ul><li>从 1 到 2，4，8，10 逐渐改变 octaves，看看会发生什么。</li><li>当 octaves 大于 4 时，尝试改变 lacunarity 的值。</li><li>当 octaves 大于 4 时，改变 gain 的值，看看会发生什么。</li></ul><p>注意，随着我们一个八度接一个八度地往上叠加，曲线看起来有越来越多的细节，同时，自相似性也越来越明显。如果你放大看看，曲线的局部和整体看起来很相似，并且，任选两段不同的部分看起来也多少有些相似。这是一个数学上的分形的重要性质，我们在上面的循环中模拟了这个性质。我们不是要创造一个<em>真的</em>分形，因为我们在几次循环之后就不再往上叠加了，但理论上说，如果我们一直继续这个循环，不断地往上叠加噪声，就会得到一个真正的数学意义上的分形。在计算机图形领域，我们能处理的细节总是有极限的，比如物体比一个像素还小的时候，所以没有必要不断地往上叠加来制造分形的形态。有时候我们确实需要叠加很多次，但不必叠加无限次。</p><p>下面的示例代码就是 fBm 的二维实现，生成了分形图案：</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/13/2d-fbm.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><ul><li>在 37 行减小八度（OCTAVES）的数量</li><li>在 47 行调试 fBm 的间隙度（lacumarity）</li><li>在 47 行调试 fBm 的增益（gain）</li></ul><p>这项技术被广泛地应用于构造程序化风景。fBm 的自相似性能够很完美地模拟山脉，因为山脉形成过程中的腐蚀形成了这种不同尺度上的自相似性。如果你对此感兴趣，你一定要去看看 <a href="http://www.iquilezles.org/www/articles/morenoise/morenoise.htm">Inigo Quiles 这篇关于高级噪声的文章</a>。</p><img src="/blog/posts/b5288859/holdsworth.jpg" class="" title="Blackout - Dan Holdsworth (..&#x2F;..&#x2F;..&#x2F;..&#x2F;Github&#x2F;thebookofshaders&#x2F;13&#x2F;holdsworth.jpg)"><p>使用相同的技术，也可以获得其他效果，比如<strong>湍流</strong>（turbulence）效果。它本质上是一个 fBm，但是由一个有符号噪声的绝对值构成，从而在函数中创建了尖锐的山谷。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; OCTAVES; i++) &#123;<br>    value += amplitude * <span class="hljs-built_in">abs</span>(snoise(st));<br>    st *= <span class="hljs-number">2.</span>;<br>    amplitude *= <span class="hljs-number">.5</span>;<br>&#125;<br></code></pre></td></tr></table></figure><p><a href="../edit.php#13/turbulence.frag"><img src="b5288859/turbulence-long-1600256780542.png"  width="520px" height="200px"></img></a></p><p>这个算法家族中的另一个成员是<strong>山脊</strong>（ridge），就是把凹下去的山谷翻上来，从而制造山脊：</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs glsl">n = <span class="hljs-built_in">abs</span>(n);     <span class="hljs-comment">// create creases</span><br>n = <span class="hljs-keyword">offset</span> - n; <span class="hljs-comment">// invert so creases are at top</span><br>n = n * n;      <span class="hljs-comment">// sharpen creases</span><br></code></pre></td></tr></table></figure><p><a href="../edit.php#13/ridge.frag"><img src="b5288859/ridge-long.png"  width="520px" height="200px"></img></a></p><p>这个算法的另外一个变种，把噪声分量乘起来（而不是叠加），可以创造一些很有用的东西。另外一个方法是，根据前一次循环中的噪声来缩放后续的噪声。当我们这样做的时候，我们已经走出严格的分形定义了，走入了一个叫“多重分形”的未知领域。多重分形虽不是按数学方式严格定义，但这并不意味着它的用处会更少些。 实际上，用多重分形模拟生成地形在商业软件中非常常见。要了解更多，你可以去读 Kenton Musgrave 的“Texturing and Modeling: a Procedural Approach”（第三版）的 16 章。很可惜，这本书几年前已经绝版，不过你还可以从图书馆和二手市场找到。（网上有卖这本书第一版的 PDF 版，但是别去买——只是浪费钱，是 1994 年的版本，不包括第三版包含的地形建模的部分。）</p><h3 id="域翘曲（Domain-Warping）"><a href="#域翘曲（Domain-Warping）" class="headerlink" title="域翘曲（Domain Warping）"></a>域翘曲（Domain Warping）</h3><p><a href="http://www.iquilezles.org/www/articles/warp/warp.htm">Inigo Quiles 写了另一篇有趣的文章</a>，关于如何用 fBm 来扭曲 fBm 空间。很有意思，不是吗？这就像《盗梦空间》里的梦中梦。</p><img src="/blog/posts/b5288859/quiles.jpg" class="" title="f(..&#x2F;..&#x2F;..&#x2F;..&#x2F;Github&#x2F;thebookofshaders&#x2F;13&#x2F;quiles.jpg) &#x3D; fbm( p + fbm( p + fbm( p ) ) ) - Inigo Quiles (2002)"><p>下面的代码展示了这项技术的一个不那么极端的例子，用它生成类似云一样的纹理。注意自相似性如何表现在结果中。</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/13/clouds.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>用这种方法用噪声扭曲纹理坐标非常有用，非常有趣，也极难掌握。这是个很强大的工具，但要想用好它需要一些经验。一个有用的办法是，用噪声的导数（梯度）替换坐标。<a href="http://evasion.imag.fr/Publications/2001/PN01/">Ken Perlin 和 Fabrice Neyret 的一篇非常著名的“流噪声”</a>就是以这个想法为基础。一些现代的 Perlin 噪声的实现不但计算噪声，还计算它的解析梯度。如果“真实”梯度对过程化函数来说不便计算，你总是可以计算出数值梯度来逼近它，尽管没那么精确而且要花费更多工夫。</p>]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
    <tags>
      
      <tag>着色器之书</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>着色器之书12</title>
    <link href="/blog/posts/c22fb8cf.html"/>
    <url>/blog/posts/c22fb8cf.html</url>
    
    <content type="html"><![CDATA[<img src="/blog/posts/c22fb8cf/dragonfly.jpg" class=""><h2 id="网格噪声（Cellular-Noise）"><a href="#网格噪声（Cellular-Noise）" class="headerlink" title="网格噪声（Cellular Noise）"></a>网格噪声（Cellular Noise）</h2><p>1996 年，在原始的 Perlin Noise 发布六年后，Perlin 的 Simplex Noise 发布五年前，<a href="http://www.rhythmiccanvas.com/research/papers/worley.pdf">Steven Worley 写了一篇名为“A Cellular Texture Basis Function”的论文</a>。在这篇论文里，他描述了一种现在被广泛使用的程序化纹理技术。</p><p>要理解它背后的原理，我们需要从<strong>迭代</strong>开始思考。你可能已经知道迭代是什么意思：对，就是使用 <code>for</code> 循环。GLSL 的 <code>for</code> 循环中，只有一个需要注意的：我们检查循环是否继续的次数必须是一个常数（<code>const</code>）. 所以，没有动态循环——迭代的次数必须是固定的。</p><p>网格噪声基于距离场，这里的距离是指到一个特征点集最近的点的距离。比如说我们要写一个 4 个特征点的距离场，我们应该做什么呢？<strong>对每一个像素，计算它到最近的特征点的距离</strong>。也就是说，我们需要遍历所有 4 个特征点，计算他们到当前像素点的距离，并把最近的那个距离存下来。 </p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">float</span> min_dist = <span class="hljs-number">100.</span>; <span class="hljs-comment">// A variable to store the closest distance to a point</span><br><br>min_dist = <span class="hljs-built_in">min</span>(min_dist, <span class="hljs-built_in">distance</span>(st, point_a));<br>min_dist = <span class="hljs-built_in">min</span>(min_dist, <span class="hljs-built_in">distance</span>(st, point_b));<br>min_dist = <span class="hljs-built_in">min</span>(min_dist, <span class="hljs-built_in">distance</span>(st, point_c));<br>min_dist = <span class="hljs-built_in">min</span>(min_dist, <span class="hljs-built_in">distance</span>(st, point_d));<br></code></pre></td></tr></table></figure><img src="/blog/posts/c22fb8cf/cell-00.png" class=""><p>这种做法不是很优雅，但至少行得通。现在让我们用数组和 <code>for</code> 循环重写。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">float</span> m_dist = <span class="hljs-number">100.</span>;  <span class="hljs-comment">// minimum distance</span><br><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i &lt; TOTAL_POINTS; i++) &#123;<br>    <span class="hljs-type">float</span> dist = <span class="hljs-built_in">distance</span>(st, <span class="hljs-keyword">points</span>[i]);<br>    m_dist = <span class="hljs-built_in">min</span>(m_dist, dist);<br>&#125;<br></code></pre></td></tr></table></figure><p>注意看我们用一个 <code>for</code> 循环遍历特征点集的数组，用一个 <a href="../glossary/?search=min"><code>min()</code></a> 函数来获得最小距离。下面是以上想法的简要的实现：</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/12/cellnoise-00.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>上面的代码中，其中一个特征点分配给了鼠标位置。把鼠标放上去玩一玩，你可以更直观地了解上面的代码是如何运行的。然后试试：</p><ul><li>你可以让其余的几个特征点也动起来吗？</li><li>在读完<a href="../07/?lan=ch">关于形状的章节</a>后，想象一些关于距离场的有意思的用法。</li><li>如果你要往距离场里添加更多的特征点怎么办？如果我们想动态地添加减少特征点数怎么办？</li></ul><h3 id="平铺和迭代"><a href="#平铺和迭代" class="headerlink" title="平铺和迭代"></a>平铺和迭代</h3><p>你可能注意到 GLSL 对 <code>for</code> 循环和 <em>数组</em> 似乎不太友好。如前所说，循环不接受动态的迭代次数。还有，遍历很多实例会显著地降低着色器的性能。这意味着我们不能把这个方法用在很大的特征点集上。我们需要寻找另一个策略，一个能利用 GPU 并行架构优势的策略。</p><img src="/blog/posts/c22fb8cf/cell-01.png" class=""><p>解决这个问题的一个方法是把空间分割成网格。并不需要计算每一个像素点到每一个特征点的距离，对吧？已经知道每个像素点是在自己的线程中运行，我们可以把空间分割成网格（cells），每个网格对应一个特征点。另外，为避免网格交界区域的偏差，我们需要计算像素点到相邻网格中的特征点的距离。这就是 <a href="http://www.rhythmiccanvas.com/research/papers/worley.pdf">Steven Worley 的论文</a>中的主要思想。最后，每个像素点只需要计算到九个特征点的距离：他所在的网格的特征点和相邻的八个网格的特征点。我们已经在<a href="../09/?lan=ch">图案</a>，<a href="../10/?lan=ch">随机</a>和<a href="../11/?lan=ch">噪声</a>这些章节介绍了如何把空间分割成网格，希望你已经熟悉这项技术。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-comment">// Scale</span><br>st *= <span class="hljs-number">3.</span>;<br><br><span class="hljs-comment">// Tile the space</span><br><span class="hljs-type">vec2</span> i_st = <span class="hljs-built_in">floor</span>(st);<br><span class="hljs-type">vec2</span> f_st = <span class="hljs-built_in">fract</span>(st);<br></code></pre></td></tr></table></figure><p>那么，计划是什么呢？我们将使用网格坐标（存储在整数坐标 <code>i_st</code> 中）来构造特征点的随机位置。<code>random2f</code> 函数接受一个 <code>vec2</code> 类型参数，返回给我们一个 <code>vec2</code> 类型的随机位置。所以，在每个网格内，我们有一个特征点在随机位置上。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">vec2</span> point = random2(i_st);<br></code></pre></td></tr></table></figure><p>网格内的每个像素点（存储在浮点坐标 <code>f_st</code> 中）都会计算它到那个随机点的距离。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">vec2</span> diff = point - f_st;<br><span class="hljs-type">float</span> dist = <span class="hljs-built_in">length</span>(diff);<br></code></pre></td></tr></table></figure><p>结果看起来就像这样：</p><p><a href="../edit.php#12/cellnoise-01.frag"><img src="c22fb8cf/cellnoise.png"  width="520px" height="200px"></img></a></p><p>我们还需要计算像素点到相邻网格中随机点的距离，而不只是当前的网格。我们需要 <strong>遍历</strong> 所有相邻网格。不是所有网格，仅仅是那些和当前网格相邻的网格。从网格坐标来说，就是 <code>x</code> 坐标从 <code>-1</code> （左）到 <code>1</code> （右）， <code>y</code> 坐标从 <code>-1</code> （下）到 <code>1</code> （上）。一个 9 个网格的 3x3 区域可以用两个 <code>for</code> 循环遍历：</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> y= <span class="hljs-number">-1</span>; y &lt;= <span class="hljs-number">1</span>; y++) &#123;<br>    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> x= <span class="hljs-number">-1</span>; x &lt;= <span class="hljs-number">1</span>; x++) &#123;<br>        <span class="hljs-comment">// Neighbor place in the grid</span><br>        <span class="hljs-type">vec2</span> neighbor = <span class="hljs-type">vec2</span>(<span class="hljs-type">float</span>(x),<span class="hljs-type">float</span>(y));<br>        ...<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><img src="/blog/posts/c22fb8cf/cell-02.png" class=""><p>现在，我们可以在双 <code>for</code> 循环中计算相邻网格中随机点的位置，只需要加上相邻网格对当前网格的偏移量。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs glsl">...<br><span class="hljs-comment">// Random position from current + neighbor place in the grid</span><br><span class="hljs-type">vec2</span> point = random2(i_st + neighbor);<br>...<br></code></pre></td></tr></table></figure><p>剩下的部分就是计算像素点到那个随机点的距离，并把最近的距离存到变量 <code>m_dist</code>（minimum distance）里面.</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs glsl">...<br><span class="hljs-type">vec2</span> diff = neighbor + point - f_st;<br><br><span class="hljs-comment">// Distance to the point</span><br><span class="hljs-type">float</span> dist = <span class="hljs-built_in">length</span>(diff);<br><br><span class="hljs-comment">// Keep the closer distance</span><br>m_dist = <span class="hljs-built_in">min</span>(m_dist, dist);<br>...<br></code></pre></td></tr></table></figure><p>上面的代码源自<a href="http://www.iquilezles.org/www/articles/smoothvoronoi/smoothvoronoi.htm">这篇 Inigo’s Quilez 的文章</a>，他写道：</p><p><em>“可能值得注意的是，上面的代码中有一个很漂亮的技巧。多数实现都存在精度问题，因为他们是在“域”空间（如“世界”或“对象”空间）内产生随机点，这可能是里原点任意远的。要解决这个问题，可以使用更高精度的数据类型，或更聪明些。我的实现不是在“域”空间（如“世界”或“对象”空间）内产生随机点，而是在“网格”空间内：一旦提取了着色点的整数和小数部分，我们当前的网格就确定了，我们所关心的就是这个网格周围发生了什么，意味着我们可以将所有坐标的整数部分放在一起，从而节省了许多精度位。事实上，一个常规的 voronoi 实现中，从着色点减去随机特征点时，点坐标的整数部分简单地消除掉了。上面的实现中，我们甚至不会让这种消除发生，因为我们正在把所有的计算移到“网格”空间。这个技巧可以让你处理这种情况： 你想要把 voronoi 用在整个星球上——可以简单地将输入替换为双精度，执行 floor() 和 fract() 计算，其余的计算仍使用浮点数，省去了将整个实现改成双精度的成本。当然，同样的技巧也适用于 Perlin Noise 模式（但是我还没有在任何地方看到过它的实现或记录）。”</em></p><p>简要重述一遍：我们把空间分割成网格，计算每个像素点到它所在网格中的那个特征点的距离，和它到相邻的八个网格中的特征点的距离，结果是一个距离场，如下所示：</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/12/cellnoise-02.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>进一步探索:</p><ul><li>缩放空间。</li><li>你有其它办法让那些特征点动起来吗？</li><li>如果我们想要加入一个鼠标位置作为其中一个特征点怎么办？</li><li>有没有其它办法构造这个距离场，除了 <code>m_dist = min(m_dist, dist);</code> 之外？</li><li>用这个距离场你可以创造出什么有意思的图案？</li></ul><p>这个算法也可以从特征点而非像素点的角度理解。在那种情况下，算法可以表述为：每个特征点向外扩张生长，直到它碰到其它扩张的区域。这反映了自然界的生长规则。生命的形态是由内部扩张、生长的力量和限制性的外部力量共同决定的。模拟这种行为的算法以 <a href="https://en.wikipedia.org/wiki/Georgy_Voronoy">Georgy Voronoi</a> 命名。</p><img src="/blog/posts/c22fb8cf/monokot_root.jpg" class=""><h3 id="Voronoi-算法"><a href="#Voronoi-算法" class="headerlink" title="Voronoi 算法"></a>Voronoi 算法</h3><p>用网格噪声构造 Voronoi 图远没有看上去的那么难。我们只需要<em>保留</em>一些关于最近的特征点的额外信息。我们将要用到一个叫 <code>m_point</code> 的 <code>vec2</code> 类型变量存储像素点到最近的特征点的向量，而不只是距离。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs glsl">...<br><span class="hljs-keyword">if</span>( dist &lt; m_dist ) &#123;<br>    m_dist = dist;<br>    m_point = point;<br>&#125;<br>...<br></code></pre></td></tr></table></figure><p>注意在下面的代码中，我们不再使用 <code>min</code> 来计算最近距离，而是用一个普通的 <code>if</code> 语句。为什么？因为当一个新的更近的特征点出现的时候，我们还需要保存它的位置（32 行至 37行）。</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/12/vorono-00.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>注意那个移动的（鼠标位置下面那个）细胞的颜色是如何根据它的位置而改变的。那是因为它的颜色由最近特征点决定。</p><p>就像我们之前所做的那样，现在是扩大规模的时候，转而使用 <a href="http://www.rhythmiccanvas.com/research/papers/worley.pdf">Steven Worley 的论文中的方法</a>。试着自己实现它。你可以通过点击下面的示例来获取帮助。注意 Steven Worley 的原始方法中，每个网格的特征点数是可变的，对大多数网格来说不止一个。在他的 C 语言实现中，这是用来提早退出来加速循环。GLSL 循环不允许动态的迭代次数，所以你可能更希望一个网格对应一个特征点。</p><div class="container" style="margin:0;padding:0">   <a href="../edit.php#12/vorono-01.frag"><canvas id="custom" class="canvas" data-fragment-url="vorono-01.frag"  style="width:100%;height:auto;margin-bottom:10px"></canvas></a></div><p>一旦你弄清楚了这个算法，想想它有什么有趣、有创意的用途。</p><img src="/blog/posts/c22fb8cf/solas.png" class="" title="Extended Voronoi - Leo Solaas (..&#x2F;..&#x2F;..&#x2F;..&#x2F;Github&#x2F;thebookofshaders&#x2F;12&#x2F;solas.png)"><img src="/blog/posts/c22fb8cf/saraceno.jpg" class="" title="Cloud Cities - Tomás Saraceno (..&#x2F;..&#x2F;..&#x2F;..&#x2F;Github&#x2F;thebookofshaders&#x2F;12&#x2F;saraceno.jpg)"><img src="/blog/posts/c22fb8cf/accretion.jpg" class="" title="Accretion Disc Series - Clint Fulkerson"><img src="/blog/posts/c22fb8cf/reza.png" class="" title="Vonoroi Puzzle - Reza Ali (..&#x2F;..&#x2F;..&#x2F;..&#x2F;Github&#x2F;thebookofshaders&#x2F;12&#x2F;reza.png)"><h3 id="优化-Voronoi"><a href="#优化-Voronoi" class="headerlink" title="优化 Voronoi"></a>优化 Voronoi</h3><p>在 2011 年, <a href="http://webstaff.itn.liu.se/~stegu/GLSL-cellular/GLSL-cellular-notes.pdf">Stefan Gustavson 优化了 Steven Worley 的算法</a>，仅仅对一个 2x2 的矩阵作遍历（而不是 3x3 的矩阵）。这显著地减少了工作量，但是会在网格边缘制造人工痕迹。看看下面的例子。</p><div class="glslGallery" data="12/2d-cnoise-2x2,12/2d-cnoise-2x2x2,12/2d-cnoise,12/3d-cnoise" data-properties="clickRun:editor,openFrameIcon:false"></div><p>Later in 2012 <a href="http://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm">Inigo Quilez wrote an article on how to make precise Voronoi borders</a>.</p><p><a href="../edit.php#12/2d-voronoi.frag"><img src="c22fb8cf/2d-voronoi.gif"  width="520px" height="200px"></img></a></p><p>Inigo 在 Voronoi 上的实验并没有就此停止。2014 年，他写了一篇非常漂亮的文章，提出一种他称作为 <a href="http://www.iquilezles.org/www/articles/voronoise/voronoise.htm">voro-noise</a> 的噪声，可以让常规噪声和 voronoi 逐渐地融合。用他的话说：</p><p><em>“尽管有这样的相似之处，但事实上，两种模式中网格的使用方式都是不同的。噪声会内插或平均随机值（如值噪声），而 Voronoi 是计算到最近特征点的距离。平滑双线性插值和最小值评估是两个非常不同的操作，或者……它们是否能用更广义的方法组合？如果是这样，那么噪声和 Voronoi 模式都可以被看作是一种更一般的以网格为基础的模式生成器？”</em></p><div class="container" style="margin:0;padding:0">   <a href="../edit.php#12/2d-voronoise.frag"><canvas id="custom" class="canvas" data-fragment-url="2d-voronoise.frag"  style="width:100%;height:auto;margin-bottom:10px"></canvas></a></div><p>现在，是时候仔细观察事物，去接受自然的启发，并用这项技术发现你自己的风景！</p><img src="/blog/posts/c22fb8cf/DeyrolleFilm.png" class="" title="Deyrolle glass film - 1831"><div class="glslGallery" data="12/metaballs,12/stippling,12/cell,12/tissue,12/cracks,160504143842" data-properties="clickRun:editor,openFrameIcon:false"></div>]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
    <tags>
      
      <tag>着色器之书</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>着色器之书11</title>
    <link href="/blog/posts/5b26e975.html"/>
    <url>/blog/posts/5b26e975.html</url>
    
    <content type="html"><![CDATA[<img src="/blog/posts/5b26e975/mcb.jpg" class="" title="NASA &#x2F; WMAP science team"><h2 id="Noise-噪声"><a href="#Noise-噪声" class="headerlink" title="Noise 噪声"></a>Noise 噪声</h2><p>稍事休息，我们来转换一下思路。我们已经玩过了看起来像电视白噪音的 random 函数，尽管脑子里还在嗡嗡地转着 shader，但是已经眼花缭乱了。是时候出去走走了。</p><p>我们感知得到浮动在皮肤上的空气，晒在脸上的阳光。世界如此生动而丰富。颜色，质地，声音。当我们走在路上，不免会注意到路面、石头、树木和云朵的表面的样子。</p><img src="/blog/posts/5b26e975/texture-00.jpg" class=""><img src="/blog/posts/5b26e975/texture-01.jpg" class=""><img src="/blog/posts/5b26e975/texture-02.jpg" class=""><img src="/blog/posts/5b26e975/texture-03.jpg" class=""><img src="/blog/posts/5b26e975/texture-04.jpg" class=""><img src="/blog/posts/5b26e975/texture-05.jpg" class=""><img src="/blog/posts/5b26e975/texture-06.jpg" class=""><p>这些纹理的不可预测性可以叫做“random”(随机），但是它们看起来不像是我们之前玩的 random。「真实世界」是如此的丰富而复杂！我们如何才能才能用计算机模拟这些多样的纹理呢？</p><p>这就是 <a href="https://mrl.nyu.edu/~perlin/">Ken Perlin</a> 想要解答的问题。在20世纪80年代早期，他被委任为电影 “Tron”（电子世界争霸战）制作现实中的纹理。为了解决这个问题，他想出了一个优雅的算法，且获得了<strong>奥斯卡奖</strong>（名副其实）。</p><img src="/blog/posts/5b26e975/tron.jpg" class="" title="Disney - Tron"><p>下面这个并不是经典的 Perlin noise 算法，但是这是一个理解如何生成 noise 的好的出发点。</p><div class="container" style="margin:0;padding:0">    <div class="simpleFunction" data="        float i = floor(x);  // 整数（i 代表 integer）        float f = fract(x);  // 小数（f 代表 fraction）        y = rand(i); //rand() 在之前的章节提过        //y = mix(rand(i), rand(i + 1.0), f);        //y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));        "></div></div><p>这和之前章节我们做的事情很像。我们把单精度浮点 <code>x</code> 分割成它的整数部分 <code>i</code> 和小数部分 <code>f</code> 。我们用 <a href=".../glossary/?search=floor"><code>floor()</code></a> 获取 <code>i</code>，用 <a href=".../glossary/?search=fract"><code>fract()</code></a> 获取 <code>f</code>。然后我们 <code>rand()</code> x 的整数部分，即根据这个整数生成一个随机值。</p><p>在这之后有两个被注释掉的语句。第一句的作用是线性插值。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs glsl">y = <span class="hljs-built_in">mix</span>(rand(i), rand(i + <span class="hljs-number">1.0</span>), f);<br></code></pre></td></tr></table></figure><p>试试取消这句的注释，看一下会变成什么样子。注意我们储存在 <code>f</code> 中的 <a href=".../glossary/?search=fract"><code>fract()</code></a> 值<a href=".../glossary/?search=mix"><code>mix()</code></a>了两个随机值。</p><p>到本书的这个部分，我们知道我们可以比比线性插值做得更好，不是吗？现在试试取消第二句的注释。第二句用 <a href=".../glossary/?search=smoothstep"><code>smoothstep()</code></a>进行插值。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs glsl">y = <span class="hljs-built_in">mix</span>(rand(i), rand(i + <span class="hljs-number">1.0</span>), <span class="hljs-built_in">smoothstep</span>(<span class="hljs-number">0.</span>,<span class="hljs-number">1.</span>,f));<br></code></pre></td></tr></table></figure><p>在取消注释后，注意顶点的变化如何变得顺滑了起来。在一些 noise 的应用中你会发现程序员喜欢用他们自己的<strong>三次多项式函数</strong>（比如下面的例子），而不是用 <a href=".../glossary/?search=smoothstep"><code>smoothstep()</code></a>。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">float</span> u = f * f * (<span class="hljs-number">3.0</span> - <span class="hljs-number">2.0</span> * f ); <span class="hljs-comment">// custom cubic curve</span><br>y = <span class="hljs-built_in">mix</span>(rand(i), rand(i + <span class="hljs-number">1.0</span>), u); <span class="hljs-comment">// using it in the interpolation</span><br></code></pre></td></tr></table></figure><p>这种 smooth randomness（平滑后的随机值）是一个图形工程师或者说艺术家的制胜法宝——它能生成非常有机的图像或几何形态。Perlin 的 noise 算法被无数次用到各种语言和各种维度的设计中，制作出无数迷人的作品。</p><img src="/blog/posts/5b26e975/robert_hodgin.jpg" class="" title="Robert Hodgin - Written Images (..&#x2F;..&#x2F;..&#x2F;..&#x2F;Github&#x2F;thebookofshaders&#x2F;11&#x2F;robert_hodgin.jpg)"><p>现在轮到你了：</p><ul><li><p>写你自己的 <code>float noise(float x)</code> 函数。</p></li><li><p>用你的 noise 函数为图形制作动效，可以移动，旋转或改变大小。</p></li><li><p>用 noise 让一群图形一起“跳舞”。</p></li><li><p>用 noise 制做有机的形态。</p></li><li><p>创作一个“生物”，给它添加更多独特的动作，使其成为独特的角色。</p></li></ul><h2 id="2D-Noise"><a href="#2D-Noise" class="headerlink" title="2D Noise"></a>2D Noise</h2><img src="/blog/posts/5b26e975/02.png" class=""><p>现在我们知道了如何在一维使用 noise，是时候进入二维世界了。在 2D 中，除了在一条线的两点（<code>fract(x)</code> 和 <code>fract(x)+1.0</code>）中插值，我们将在一个平面上的方形的四角（<code>fract(st)</code>， <code>fract(st)+vec2(1.,0.)</code>， <code>fract(st)+vec2(0.,1.)</code> 和 <code>fract(st)+vec2(1.,1.)</code>）中插值。</p><img src="/blog/posts/5b26e975/01.png" class=""><p>同样，如果我们想要在三维中使用 noise，就需要在一个立方体的8个角中插值。这个技术的重点在于插入随机值，所以我们叫它 <strong>value noise</strong>。</p><img src="/blog/posts/5b26e975/04.jpg" class=""><p>就像一维的那个例子，这个插值不是线性的，而是三次方的，它会平滑地在方形网格中插入点。</p><img src="/blog/posts/5b26e975/05.jpg" class=""><p>让我们来看一下这个方程。</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/11/2d-noise.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>我们先把空间大小变成五倍（第 45 行）以便看清栅格间的插值。然后在 noise 函数中我们把空间分成更小的单元。我们把它的整数部分和非整数部分都储存在这个单元里。我们计算整数位置的顶点的坐标，并给每个顶点生成一个随机值（第 23 - 26 行）。最后，在第 35 行用我们之前储存的小数位置的值，在四个顶点的随机值之间插值。</p><p>现在又到你了。试试下面的练习：</p><ul><li><p>改变第 45 行的乘数。添加动态试试。</p></li><li><p>缩放到什么程度会让 noise 再度变得像 random？</p></li><li><p>缩放到什么程度就看不出来 noise 了？</p></li><li><p>试试看把 noise 函数和鼠标位置连起来。</p></li><li><p>如果我们把 noise 的斜率处理成距离场（distance field）会怎么样？用它来做点有趣的事情。</p></li><li><p>现在你已经可以在混沌和有序之间进行一些操控了，是时候把这些知识用起来了。用长方形，色彩，和 noise 做一幅 <a href="http://en.wikipedia.org/wiki/Mark_Rothko">Mark Rothko</a> 的复杂的画作吧。</p></li></ul><img src="/blog/posts/5b26e975/rothko.jpg" class="" title="Mark Rothko - Three (..&#x2F;..&#x2F;..&#x2F;..&#x2F;Github&#x2F;thebookofshaders&#x2F;11&#x2F;rothko.jpg)"><h2 id="生成式设计中的-noise-应用"><a href="#生成式设计中的-noise-应用" class="headerlink" title="生成式设计中的 noise 应用"></a>生成式设计中的 noise 应用</h2><p>Noise 算法的设计初衷是将难以言说的自然质感转化成数字图像。在目前我们看到的一维和二维的实践中，都是在random <em>values</em>（随机值）之间插值，所以它们才被叫做 <strong>Value Noise</strong>，但是还有很多很多获取 noise 的方法……</p><p><a href="../edit.php#11/2d-vnoise.frag"> <img src="/blog/posts/5b26e975/value-noise.png" class="" title="Inigo Quilez - Value Noise"> </a></p><p>如你所见，value noise 看起来非常“块状”。为了消除这种块状的效果，在 1985 年 <a href="https://mrl.nyu.edu/~perlin/">Ken Perlin</a> 开发了另一种 noise 算法 <strong>Gradient Noise</strong>。Ken 解决了如何插入随机的 <em>gradients</em>（梯度、渐变）而不是一个固定值。这些梯度值来自于一个二维的随机函数，返回一个方向（<code>vec2</code> 格式的向量），而不仅是一个值（<code>float</code>格式）。点击下面的图片查看代码，看这个函数是如何运作的。</p><p><a href="../edit.php#11/2d-gnoise.frag"> <img src="/blog/posts/5b26e975/gradient-noise.png" class="" title="Inigo Quilez - Gradient Noise"> </a></p><p>花一分钟来看看 <a href="http://www.iquilezles.org/">Inigo Quilez</a> 做的两个例子，注意 <a href="https://www.shadertoy.com/view/lsf3WH">value noise</a> 和 <a href="https://www.shadertoy.com/view/XdXGW8">gradient noise</a>的区别。</p><p>就像一个画家非常了解画上的颜料是如何晕染的，我们越了解 noise 是如何运作的，越能更好地使用 noise。比如，如果我们要用一个二维的 noise 来旋转空间中的直线，我们就可以制作下图的旋涡状效果，看起来就像木头表皮一样。同样地，你可以点击图片查看代码。</p><p><a href="../edit.php#11/wood.frag"> <img src="/blog/posts/5b26e975/wood-long.png" class="" title="Wood texture"> </a></p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs glsl">pos = rotate2d( noise(pos) ) * pos; <span class="hljs-comment">// 旋转空间</span><br>pattern = <span class="hljs-keyword">lines</span>(pos,<span class="hljs-number">.5</span>); <span class="hljs-comment">// 画直线</span><br></code></pre></td></tr></table></figure><p>另一种用 noise 制作有趣的图案的方式是用 distance field（距离场）处理它，用用 <a href="../07/">第七章</a>提到的招数。</p><p><a href="../edit.php#11/splatter.frag"> <img src="/blog/posts/5b26e975/splatter-long.png" class="" title="Splatter texture"> </a></p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs glsl">color += <span class="hljs-built_in">smoothstep</span>(<span class="hljs-number">.15</span>,<span class="hljs-number">.2</span>,noise(st*<span class="hljs-number">10.</span>)); <span class="hljs-comment">// 黑色的泼溅点</span><br>color -= <span class="hljs-built_in">smoothstep</span>(<span class="hljs-number">.35</span>,<span class="hljs-number">.4</span>,noise(st*<span class="hljs-number">10.</span>)); <span class="hljs-comment">// 泼溅点上的洞</span><br></code></pre></td></tr></table></figure><p>第三种方法是用 noise 函数来变换一个形状。这个也需要我们在<a href="../07/">第七章</a>学到的技术。</p><p><a href="../edit.php#11/circleWave-noise.frag"><canvas id="custom" class="canvas" data-fragment-url="circleWave-noise.frag"  width="300px" height="300"></canvas></a></p><p>给你的练习：</p><ul><li><p>你还能做出什么其他图案呢？花岗岩？大理石？岩浆？水？找三种你感兴趣的材质，用 noise 加一些算法把它们做出来。</p></li><li><p>用 noise 给一个形状变形。</p></li><li><p>把 noise 加到动作中会如何？回顾<a href="../08/">第八章</a>。用移动 “+” 四处跑的那个例子，加一些 random 和 noise 进去。</p></li><li><p>用代码生成波洛克（Jackson Pollock）的画。</p></li></ul><img src="/blog/posts/5b26e975/pollock.jpg" class="" title="Jackson Pollock - Number 14 gray (..&#x2F;..&#x2F;..&#x2F;..&#x2F;Github&#x2F;thebookofshaders&#x2F;11&#x2F;pollock.jpg)"><h2 id="Simplex-Noise"><a href="#Simplex-Noise" class="headerlink" title="Simplex Noise"></a>Simplex Noise</h2><p>对于 Ken Perlin 来说他的算法所取得的成功是远远不够的。他觉得可以更好。在 2001 年的 Siggraph（译者注：Siggraph是由美国计算机协会「计算机图形专业组」组织的计算机图形学顶级年度会议）上，他展示了 “simplex noise”，simplex noise 比之前的算法有如下优化：</p><ul><li>它有着更低的计算复杂度和更少乘法计算。</li><li>它可以用更少的计算量达到更高的维度。</li><li>制造出的 noise 没有明显的人工痕迹。</li><li>有着定义得很精巧的连续的 gradients（梯度），可以大大降低计算成本。</li><li>特别易于硬件实现。</li></ul><p>我知道你一定在想：“这人是谁？”是的，他的工作非常杰出！但是说真的，他是如何优化算法的呢？我们已经知道在二维中他是如何在四个点（正方形的四个角）之间插值的；所以没错你已经猜到了，对于三维<a href="../edit.php#11/3d-noise.frag">（这里有个示例）</a>和四维我们需要插入 8 个和 16 个点。对吧？也就是说对于 N 维你需要插入 2 的 n 次方个点（2^N）。但是 Ken 很聪明地意识到尽管很显然填充屏幕的形状应该是方形，在二维中最简单的形状却是等边三角形。所以他把正方形网格（我们才刚学了怎么用）替换成了单纯形等边三角形的网格。</p><img src="/blog/posts/5b26e975/simplex-grid-00.png" class=""><p>这时 N 维的形状就只需要 N + 1 个点了。也就是说在二维中少了 1 个点，三维中少了 4 个，四维中则少了 11 个！巨大的提升！</p><p>在二维中插值过程和常规的 noise 差不多，通过在一组点之间插值。但是在这种情况下，改用单纯形网格，我们只需要给总共 3 个点插值。</p><img src="/blog/posts/5b26e975/simplex-grid-01.png" class=""><p>这个单纯形网格是如何制作的？这是另一个聪明绝顶而十分优雅的做法。可以先把常规的四角网格分成两个等腰三角形，然后再把三角形歪斜成等边三角形。</p><img src="/blog/posts/5b26e975/simplex-grid-02.png" class=""><p>然后，就像 <a href="http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf">Stefan Gustavson 在这篇文献中说的</a>：  <strong>“……通过观察转换后坐标的整数部分，我们就可以快速地判断哪个包含着两个单纯形的单元含有我们所需的点。并且通过比较 x 和 y 的大小，我们就可以判断这个点是在上三角还是下三角中，并且遍历这个正确的三角形。”</strong></p><p>在下面的代码中你可以取消第 44 行的注释，看一看网格是如何歪斜的，然后取消第 47 行的注释，看一看如何建造单纯形网格。注意第 22 行中我们仅仅通过判断 if <code>x &gt; y</code> (下三角) 还是 <code>y &gt; x</code> (上三角)，就把歪斜过的正方形切成了两个等腰三角形。</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/11/simplex-grid.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>另一个 <strong>Simplex Noise</strong> 的优化是把三次 Hermite 函数（Cubic Hermite Curve：<em>f(x) = 3x^2-2x^3</em>，和 <a href=".../glossary/?search=smoothstep"><code>smoothstep()</code></a> 一样）替换成了四次 Hermite 函数（ <em>f(x) = 6x^5-15x^4+10x^3</em> ）。这就使得函数曲线两端更“平”，所以每个格的边缘更加优雅地与另一个衔接。也就是说格子的过渡更加连续。你可以取消下面例子的第二个公式的注释，亲眼看看其中的变化（或者看<a href="https://www.desmos.com/calculator/2xvlk5xp8b">这个例子</a>）。</p><div class="container" style="margin:0;padding:0">    <div class="simpleFunction" data="        // 三次 Hermite 曲线。和 SmoothStep() 一样        y = x*x*(3.0-2.0*x);        // 四次 Hermite 曲线        //y = x*x*x*(x*(x*6.-15.)+10.);        "></div></div><p>注意曲线的末端发生了怎样的变化。你可以阅读 <a href="http://mrl.nyu.edu/~perlin/paper445.pdf">Ken 自己的解释</a>了解更多。</p><p>所有这些进展汇聚成了算法中的杰作 <strong>Simplex Noise</strong>。下面是这个算法在 GLSL 中的应用，作者是 Ian McEwan，以<a href="http://webstaff.itn.liu.se/~stegu/jgt2012/article.pdf">这篇论文</a>发表，对于我们的教学而言太复杂了，但你可以点开看看，也许没有你想象得那么晦涩难懂。</p><p><a href="../edit.php#11/2d-snoise-clear.frag"> <img src="/blog/posts/5b26e975/simplex-noise.png" class="" title="Ian McEwan of Ashima Arts - Simplex Noise"> </a></p><p>好了，技术细节就说到这里，现在你可以利用它好好自由发挥一下：</p><ul><li><p>凝神思考每个 noise 的实例的模样。设想它们是你的原材料，就像雕塑家手中的大理石。你觉得每一个带给你怎样不同的“感觉“？眯起你的眼睛释放想象力，就像你在观察云朵的形状的时候那样。你看到了什么？你想起了什么？你觉得每个 noise 生成的图像可以用来做成什么？放开胆量去做吧，用代码实现它。</p></li><li><p>做一个 shader 来表现流体的质感。比如像<a href="https://en.wikipedia.org/wiki/Lava_lamp?oldformat=true">熔岩灯</a>，墨水滴，水，等等。</p></li></ul><div class="container" style="margin:0;padding:0">   <a href="../edit.php#11/lava-lamp.frag"><canvas id="custom" class="canvas" data-fragment-url="lava-lamp.frag"  style="width:100%;height:auto;margin-bottom:10px"></canvas></a></div><ul><li>用 Simplex Noise 给你现在的作品添加更多的材质效果。</li></ul><div class="container" style="margin:0;padding:0">   <a href="../edit.php#11/iching-03.frag"><canvas id="custom" class="canvas" data-fragment-url="iching-03.frag"  style="width:100%;height:auto;margin-bottom:10px"></canvas></a></div><p>在本章我们介绍了一些操控混沌的方法。这并不是一件简单的工作！成为 noise 超级大师需要时间和努力。</p><p>在下面的章节我们会看到一些很有名的技术，来修葺你的技能树，并且从 noise 中学到更多，并利用 shader 设计出更多优质的生成式艺术作品。在那之前，去外面走走，享受深入思考自然和错综复杂的图案的时光吧。培养洞察力也许需要和动手能力相同（甚至更多）的努力。出门走走享受今天剩余的时光吧！</p><p style="text-align:center; font-style: italic;">“和树聊聊天吧，和它交个朋友。” Bob Ross</p>]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
    <tags>
      
      <tag>着色器之书</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>shader常用的词汇表</title>
    <link href="/blog/posts/shaderglossary.html"/>
    <url>/blog/posts/shaderglossary.html</url>
    
    <content type="html"><![CDATA[<h2 id="类型"><a href="#类型" class="headerlink" title="类型"></a>类型</h2><h3 id="void"><a href="#void" class="headerlink" title="void"></a>void</h3><h3 id="bool"><a href="#bool" class="headerlink" title="bool"></a>bool</h3><h3 id="int"><a href="#int" class="headerlink" title="int"></a>int</h3><h3 id="float"><a href="#float" class="headerlink" title="float"></a>float</h3><h3 id="bvec2"><a href="#bvec2" class="headerlink" title="bvec2"></a>bvec2</h3><h3 id="bvec3"><a href="#bvec3" class="headerlink" title="bvec3"></a>bvec3</h3><h3 id="bvec4"><a href="#bvec4" class="headerlink" title="bvec4"></a>bvec4</h3><h3 id="ivec2"><a href="#ivec2" class="headerlink" title="ivec2"></a>ivec2</h3><h3 id="ivec3"><a href="#ivec3" class="headerlink" title="ivec3"></a>ivec3</h3><h3 id="ivec4"><a href="#ivec4" class="headerlink" title="ivec4"></a>ivec4</h3><h3 id="mat2"><a href="#mat2" class="headerlink" title="mat2"></a>mat2</h3><h3 id="mat3"><a href="#mat3" class="headerlink" title="mat3"></a>mat3</h3><h3 id="mat4"><a href="#mat4" class="headerlink" title="mat4"></a>mat4</h3><p><code>4x4</code>浮点矩阵</p><p>声明</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">mat4</span> aMat4 = <span class="hljs-type">mat4</span>(<span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>,  <span class="hljs-comment">// 1. column</span><br>                  <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>,  <span class="hljs-comment">// 2. column</span><br>                  <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>,  <span class="hljs-comment">// 3. column</span><br>                  <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>); <span class="hljs-comment">// 4. column</span><br><span class="hljs-type">mat4</span> bMat4 = <span class="hljs-type">mat4</span>(<span class="hljs-number">1.0</span>);<br><span class="hljs-type">mat4</span> cMat4 = <span class="hljs-type">mat4</span>(aVec4, bVec4, cVec4, dVec4);<br><span class="hljs-type">mat4</span> dMat4 = <span class="hljs-type">mat4</span>(aVec4, aVec3, bVec4, cVec4, aFloat);<br></code></pre></td></tr></table></figure><p><code>mat4</code>数据类型是由浮点<code>4x4</code>矩阵组成的。如上所示，可以通过不同的方式初始化： </p><ul><li>逐列为每个组件提供值。 </li><li>提供一个用于主对角线上的组件的值。 </li><li>提供向量和标量的组合。 </li></ul><p>以同样的方式，可以按组件方式或按列访问数据： </p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs glsl">aMat4[<span class="hljs-number">3</span>][<span class="hljs-number">3</span>] = <span class="hljs-number">1.0</span>;<br><span class="hljs-type">float</span> aFloat = aMat4[<span class="hljs-number">3</span>][<span class="hljs-number">3</span>];<br><br>aMat4[<span class="hljs-number">0</span>] = <span class="hljs-type">vec4</span>(<span class="hljs-number">1.0</span>);<br><span class="hljs-type">vec4</span> aVec4 = aMat4[<span class="hljs-number">0</span>];<br></code></pre></td></tr></table></figure><h3 id="smapler2D"><a href="#smapler2D" class="headerlink" title="smapler2D"></a>smapler2D</h3><h3 id="smaplerCube"><a href="#smaplerCube" class="headerlink" title="smaplerCube"></a>smaplerCube</h3><h3 id="struct"><a href="#struct" class="headerlink" title="struct"></a>struct</h3><p>结构变量类型,例子</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs glsl">struct matStruct &#123;<br>    <span class="hljs-type">vec4</span> ambientColor;<br>    <span class="hljs-type">vec4</span> diffuseColor;<br>    <span class="hljs-type">vec4</span> specularColor;<br>    <span class="hljs-type">float</span> specularExponent;<br>&#125; newMaterial;<br><br>newMaterial = matStruct(<span class="hljs-type">vec4</span>(<span class="hljs-number">0.1</span>, <span class="hljs-number">0.1</span>, <span class="hljs-number">0.1</span>, <span class="hljs-number">1.0</span>),<br>                        <span class="hljs-type">vec4</span>(<span class="hljs-number">1.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>),<br>                        <span class="hljs-type">vec4</span>(<span class="hljs-number">0.7</span>, <span class="hljs-number">0.7</span>, <span class="hljs-number">0.7</span>, <span class="hljs-number">1.0</span>),<br>                        <span class="hljs-number">50.0</span>);<br></code></pre></td></tr></table></figure><h4 id="描述"><a href="#描述" class="headerlink" title="描述"></a>描述</h4><p><code>struct</code>声明基于标准类型的自定义数据结构。具有相同名称的结构的构造函数将自动创建。变量的声明（在本例中为<code>newMaterial</code>）是可选的。</p><h2 id="限定词"><a href="#限定词" class="headerlink" title="限定词"></a>限定词</h2><h3 id="attribute"><a href="#attribute" class="headerlink" title="attribute"></a>attribute</h3><p>顶点属性数据。</p><p>例如:</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-keyword">attribute</span> <span class="hljs-type">vec4</span> v_color;<br></code></pre></td></tr></table></figure><p><code>attribute</code> 只读变量，包含从WebGL / OpenGL环境共享到顶点着色器的数据。</p><p>由于顶点着色器对每个顶点执行一次，因此通常为每个顶点数据指定属性，并使用以下信息：顶点的空间位置，颜色，法线方向和纹理坐标。</p><h3 id="const"><a href="#const" class="headerlink" title="const"></a>const</h3><p>常量限定词</p><p>例如:</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-keyword">const</span> <span class="hljs-type">float</span> PI = <span class="hljs-number">3.14159265359</span>;<br></code></pre></td></tr></table></figure><p>const限定符可以应用于任何变量的声明，以指定其值不会更改。</p><h3 id="uniform"><a href="#uniform" class="headerlink" title="uniform"></a>uniform</h3><p>统一变量限定符。</p><p>例子:</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-keyword">uniform</span> <span class="hljs-type">vec4</span> direction;<br></code></pre></td></tr></table></figure><p><code>uniform</code> 变量包含从WebGL / OpenGL环境共享到顶点或片段着色器的只读数据。 该值是针对每个图元的，因此对于在图元，帧或场景中保持不变的变量很有用。</p><h3 id="varying"><a href="#varying" class="headerlink" title="varying"></a>varying</h3><p>可变变量限定符。</p><p>例子:</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-keyword">varying</span> <span class="hljs-type">vec3</span> position;<br></code></pre></td></tr></table></figure><p><code>varying</code> 变量包含从顶点着色器到片段着色器共享的数据。 必须在顶点着色器中写入变量，然后从组成片段的顶点内插片段着色器中的只读值。</p><h3 id="precision"><a href="#precision" class="headerlink" title="precision"></a>precision</h3><h3 id="highp"><a href="#highp" class="headerlink" title="highp"></a>highp</h3><h3 id="mediump"><a href="#mediump" class="headerlink" title="mediump"></a>mediump</h3><h3 id="lowp"><a href="#lowp" class="headerlink" title="lowp"></a>lowp</h3><h3 id="in"><a href="#in" class="headerlink" title="in"></a>in</h3><h3 id="out"><a href="#out" class="headerlink" title="out"></a>out</h3><h3 id="inout"><a href="#inout" class="headerlink" title="inout"></a>inout</h3><h2 id="内置变量"><a href="#内置变量" class="headerlink" title="内置变量"></a>内置变量</h2><h3 id="gl-Position"><a href="#gl-Position" class="headerlink" title="gl_Position"></a>gl_Position</h3><h3 id="gl-PointSize"><a href="#gl-PointSize" class="headerlink" title="gl_PointSize"></a>gl_PointSize</h3><h3 id="gl-PointCoord"><a href="#gl-PointCoord" class="headerlink" title="gl_PointCoord"></a>gl_PointCoord</h3><h3 id="gl-FrontFacing"><a href="#gl-FrontFacing" class="headerlink" title="gl_FrontFacing"></a>gl_FrontFacing</h3><h3 id="gl-FragCoord"><a href="#gl-FragCoord" class="headerlink" title="gl_FragCoord"></a>gl_FragCoord</h3><h3 id="gl-FragColor"><a href="#gl-FragColor" class="headerlink" title="gl_FragColor"></a>gl_FragColor</h3><h2 id="内置常数"><a href="#内置常数" class="headerlink" title="内置常数"></a>内置常数</h2><h3 id="gl-MaxVertexAttribs"><a href="#gl-MaxVertexAttribs" class="headerlink" title="gl_MaxVertexAttribs"></a>gl_MaxVertexAttribs</h3><h3 id="gl-MaxVaryingVectors"><a href="#gl-MaxVaryingVectors" class="headerlink" title="gl_MaxVaryingVectors"></a>gl_MaxVaryingVectors</h3><h3 id="gl-MaxVertexTextureImageUnits"><a href="#gl-MaxVertexTextureImageUnits" class="headerlink" title="gl_MaxVertexTextureImageUnits"></a>gl_MaxVertexTextureImageUnits</h3><h3 id="gl-MaxCombinedTextureImageUnits"><a href="#gl-MaxCombinedTextureImageUnits" class="headerlink" title="gl_MaxCombinedTextureImageUnits"></a>gl_MaxCombinedTextureImageUnits</h3><h3 id="gl-MaxTextureImageUnits"><a href="#gl-MaxTextureImageUnits" class="headerlink" title="gl_MaxTextureImageUnits"></a>gl_MaxTextureImageUnits</h3><h3 id="gl-MaxFragmentUniformVectors"><a href="#gl-MaxFragmentUniformVectors" class="headerlink" title="gl_MaxFragmentUniformVectors"></a>gl_MaxFragmentUniformVectors</h3><h3 id="gl-MaxDrawBuffers"><a href="#gl-MaxDrawBuffers" class="headerlink" title="gl_MaxDrawBuffers"></a>gl_MaxDrawBuffers</h3><h2 id="角度和三角函数"><a href="#角度和三角函数" class="headerlink" title="角度和三角函数"></a>角度和三角函数</h2><h3 id="radians"><a href="#radians" class="headerlink" title="radians()"></a>radians()</h3><p>将数量转换为弧度</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">float</span> <span class="hljs-built_in">radians</span>(<span class="hljs-type">float</span> <span class="hljs-built_in">degrees</span>) <br><span class="hljs-type">vec2</span> <span class="hljs-built_in">radians</span>(<span class="hljs-type">vec2</span> <span class="hljs-built_in">degrees</span>) <br><span class="hljs-type">vec3</span> <span class="hljs-built_in">radians</span>(<span class="hljs-type">vec3</span> <span class="hljs-built_in">degrees</span>) <br><span class="hljs-type">vec4</span> <span class="hljs-built_in">radians</span>(<span class="hljs-type">vec4</span> <span class="hljs-built_in">degrees</span>)<br></code></pre></td></tr></table></figure><p>参数:<code>degrees</code>指定要转换为弧度的数量（以度为单位）。</p><p>描述:<code>radians()</code> 将以度为单位的数量转换为弧度。即返回值为<code>(PI * degrees)/180</code>。</p><h3 id="degrees"><a href="#degrees" class="headerlink" title="degrees()"></a>degrees()</h3><p>将弧度转换为度</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">float</span> <span class="hljs-built_in">degrees</span>(<span class="hljs-type">float</span> <span class="hljs-built_in">radians</span>)  <br><span class="hljs-type">vec2</span> <span class="hljs-built_in">degrees</span>(<span class="hljs-type">vec2</span> <span class="hljs-built_in">radians</span>)  <br><span class="hljs-type">vec3</span> <span class="hljs-built_in">degrees</span>(<span class="hljs-type">vec3</span> <span class="hljs-built_in">radians</span>)  <br><span class="hljs-type">vec4</span> <span class="hljs-built_in">degrees</span>(<span class="hljs-type">vec4</span> <span class="hljs-built_in">radians</span>)<br></code></pre></td></tr></table></figure><p>参数:<code>radians</code>指定要转换为度的数量（以弧度为单位）。</p><p>描述:<code>degrees()</code> 将以弧度表示的数量转换为度数。 也就是说，返回值为<code>(180.0*radians)/PI</code></p><h3 id="sin"><a href="#sin" class="headerlink" title="sin()"></a>sin()</h3><p>返回参数的正弦</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">float</span> <span class="hljs-built_in">sin</span>(<span class="hljs-type">float</span> angle)  <br><span class="hljs-type">vec2</span> <span class="hljs-built_in">sin</span>(<span class="hljs-type">vec2</span> angle)  <br><span class="hljs-type">vec3</span> <span class="hljs-built_in">sin</span>(<span class="hljs-type">vec3</span> angle)  <br><span class="hljs-type">vec4</span> <span class="hljs-built_in">sin</span>(<span class="hljs-type">vec4</span> angle)<br></code></pre></td></tr></table></figure><p>参数:<code>angle</code> 指定以弧度表示的要返回正弦的量。</p><p>描述:<code>sin()</code>返回角度的三角正弦值。</p><h3 id="cos"><a href="#cos" class="headerlink" title="cos()"></a>cos()</h3><h3 id="tan"><a href="#tan" class="headerlink" title="tan()"></a>tan()</h3><h3 id="asin"><a href="#asin" class="headerlink" title="asin()"></a>asin()</h3><h3 id="acos"><a href="#acos" class="headerlink" title="acos()"></a>acos()</h3><h3 id="atan"><a href="#atan" class="headerlink" title="atan()"></a>atan()</h3><h2 id="指数函数"><a href="#指数函数" class="headerlink" title="指数函数"></a>指数函数</h2><h3 id="pow"><a href="#pow" class="headerlink" title="pow()"></a>pow()</h3><p>将第一个参数的值返回第二个参数的幂。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">float</span> <span class="hljs-built_in">pow</span>(<span class="hljs-type">float</span> x, <span class="hljs-type">float</span> y)  <br><span class="hljs-type">vec2</span> <span class="hljs-built_in">pow</span>(<span class="hljs-type">vec2</span> x, <span class="hljs-type">vec2</span> y)  <br><span class="hljs-type">vec3</span> <span class="hljs-built_in">pow</span>(<span class="hljs-type">vec3</span> x, <span class="hljs-type">vec3</span> y)  <br><span class="hljs-type">vec4</span> <span class="hljs-built_in">pow</span>(<span class="hljs-type">vec4</span> x, <span class="hljs-type">vec4</span> y)<br></code></pre></td></tr></table></figure><h3 id="exp"><a href="#exp" class="headerlink" title="exp()"></a>exp()</h3><p>返回参数的自然幂</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">float</span> <span class="hljs-built_in">exp</span>(<span class="hljs-type">float</span> x)  <br><span class="hljs-type">vec2</span> <span class="hljs-built_in">exp</span>(<span class="hljs-type">vec2</span> x)  <br><span class="hljs-type">vec3</span> <span class="hljs-built_in">exp</span>(<span class="hljs-type">vec3</span> x)  <br><span class="hljs-type">vec4</span> <span class="hljs-built_in">exp</span>(<span class="hljs-type">vec4</span> x)<br></code></pre></td></tr></table></figure><h3 id="log"><a href="#log" class="headerlink" title="log()"></a>log()</h3><h3 id="exp2"><a href="#exp2" class="headerlink" title="exp2()"></a>exp2()</h3><h3 id="log2"><a href="#log2" class="headerlink" title="log2()"></a>log2()</h3><h3 id="sqrt"><a href="#sqrt" class="headerlink" title="sqrt()"></a>sqrt()</h3><h3 id="inversesqrt"><a href="#inversesqrt" class="headerlink" title="inversesqrt()"></a>inversesqrt()</h3><h2 id="常用函数"><a href="#常用函数" class="headerlink" title="常用函数"></a>常用函数</h2><h3 id="abs"><a href="#abs" class="headerlink" title="abs()"></a>abs()</h3><h3 id="sign"><a href="#sign" class="headerlink" title="sign()"></a>sign()</h3><h3 id="floor"><a href="#floor" class="headerlink" title="floor()"></a>floor()</h3><h3 id="ceil"><a href="#ceil" class="headerlink" title="ceil()"></a>ceil()</h3><h3 id="fract"><a href="#fract" class="headerlink" title="fract()"></a>fract()</h3><p>计算参数的小数部分</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">float</span> <span class="hljs-built_in">fract</span>(<span class="hljs-type">float</span> x)  <br><span class="hljs-type">vec2</span> <span class="hljs-built_in">fract</span>(<span class="hljs-type">vec2</span> x)  <br><span class="hljs-type">vec3</span> <span class="hljs-built_in">fract</span>(<span class="hljs-type">vec3</span> x)  <br><span class="hljs-type">vec4</span> <span class="hljs-built_in">fract</span>(<span class="hljs-type">vec4</span> x)<br></code></pre></td></tr></table></figure><p><code>x</code>指定要评估的值。</p><p>​    <code>fract()</code>返回x的小数部分。计算公式为<code>x-floor(x)</code>。</p><h3 id="mod"><a href="#mod" class="headerlink" title="mod()"></a>mod()</h3><p>计算一个参数取模的值</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">float</span> <span class="hljs-built_in">mod</span>(<span class="hljs-type">float</span> x, <span class="hljs-type">float</span> y)  <br><span class="hljs-type">vec2</span> <span class="hljs-built_in">mod</span>(<span class="hljs-type">vec2</span> x, <span class="hljs-type">vec2</span> y)  <br><span class="hljs-type">vec3</span> <span class="hljs-built_in">mod</span>(<span class="hljs-type">vec3</span> x, <span class="hljs-type">vec3</span> y)  <br><span class="hljs-type">vec4</span> <span class="hljs-built_in">mod</span>(<span class="hljs-type">vec4</span> x, <span class="hljs-type">vec4</span> y)<br><br><span class="hljs-type">vec2</span> <span class="hljs-built_in">mod</span>(<span class="hljs-type">vec2</span> x, <span class="hljs-type">float</span> y)  <br><span class="hljs-type">vec3</span> <span class="hljs-built_in">mod</span>(<span class="hljs-type">vec3</span> x, <span class="hljs-type">float</span> y)  <br><span class="hljs-type">vec4</span> <span class="hljs-built_in">mod</span>(<span class="hljs-type">vec4</span> x, <span class="hljs-type">float</span> y)<br></code></pre></td></tr></table></figure><p>参数:<code>x</code>指定要评估的值。 <code>y</code>指定要获取其模数的值。</p><p>描述:<code>mod()</code>返回x模y的值。计算为<code>x-y * floor(x / y)</code>。</p><h3 id="min"><a href="#min" class="headerlink" title="min()"></a>min()</h3><h3 id="max"><a href="#max" class="headerlink" title="max()"></a>max()</h3><h3 id="clamp"><a href="#clamp" class="headerlink" title="clamp()"></a>clamp()</h3><h3 id="mix"><a href="#mix" class="headerlink" title="mix()"></a>mix()</h3><h3 id="step"><a href="#step" class="headerlink" title="step()"></a>step()</h3><p>通过比较两个值生成阶跃函数</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">float</span> <span class="hljs-built_in">step</span>(<span class="hljs-type">float</span> edge, <span class="hljs-type">float</span> x)  <br><span class="hljs-type">vec2</span> <span class="hljs-built_in">step</span>(<span class="hljs-type">vec2</span> edge, <span class="hljs-type">vec2</span> x)  <br><span class="hljs-type">vec3</span> <span class="hljs-built_in">step</span>(<span class="hljs-type">vec3</span> edge, <span class="hljs-type">vec3</span> x)  <br><span class="hljs-type">vec4</span> <span class="hljs-built_in">step</span>(<span class="hljs-type">vec4</span> edge, <span class="hljs-type">vec4</span> x)<br><br><span class="hljs-type">vec2</span> <span class="hljs-built_in">step</span>(<span class="hljs-type">float</span> edge, <span class="hljs-type">vec2</span> x)  <br><span class="hljs-type">vec3</span> <span class="hljs-built_in">step</span>(<span class="hljs-type">float</span> edge, <span class="hljs-type">vec3</span> x)  <br><span class="hljs-type">vec4</span> <span class="hljs-built_in">step</span>(<span class="hljs-type">float</span> edge, <span class="hljs-type">vec4</span> x)<br></code></pre></td></tr></table></figure><p>参数:</p><p><code>edge</code>指定步进函数的边缘位置。</p><p><code>x</code>指定用于生成步进函数的值。</p><p>描述:</p><p><code>step()</code>通过将<code>x</code>与<code>edge</code>进行比较来生成step函数。 对于返回值的元素<code>i</code>，如果<code>x[i] &lt;edge[i]</code>返回<code>0.0</code>，否则返回<code>1.0</code>。</p><h3 id="smoothstep"><a href="#smoothstep" class="headerlink" title="smoothstep()"></a>smoothstep()</h3><p>在两个值之间执行Hermite插值</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">float</span> <span class="hljs-built_in">smoothstep</span>(<span class="hljs-type">float</span> edge0, <span class="hljs-type">float</span> edge1, <span class="hljs-type">float</span> x)  <br><span class="hljs-type">vec2</span> <span class="hljs-built_in">smoothstep</span>(<span class="hljs-type">vec2</span> edge0, <span class="hljs-type">vec2</span> edge1, <span class="hljs-type">vec2</span> x)  <br><span class="hljs-type">vec3</span> <span class="hljs-built_in">smoothstep</span>(<span class="hljs-type">vec3</span> edge0, <span class="hljs-type">vec3</span> edge1, <span class="hljs-type">vec3</span> x)  <br><span class="hljs-type">vec4</span> <span class="hljs-built_in">smoothstep</span>(<span class="hljs-type">vec4</span> edge0, <span class="hljs-type">vec4</span> edge1, <span class="hljs-type">vec4</span> x)<br><br><span class="hljs-type">vec2</span> <span class="hljs-built_in">smoothstep</span>(<span class="hljs-type">float</span> edge0, <span class="hljs-type">float</span> edge1, <span class="hljs-type">vec2</span> x)  <br><span class="hljs-type">vec3</span> <span class="hljs-built_in">smoothstep</span>(<span class="hljs-type">float</span> edge0, <span class="hljs-type">float</span> edge1, <span class="hljs-type">vec3</span> x)  <br><span class="hljs-type">vec4</span> <span class="hljs-built_in">smoothstep</span>(<span class="hljs-type">float</span> edge0, <span class="hljs-type">float</span> edge1, <span class="hljs-type">vec4</span> x)<br></code></pre></td></tr></table></figure><p>参数:</p><p><code>edge0</code>指定Hermite函数下边缘的值。</p><p><code>edge1</code>指定Hermite函数的上边缘的值。</p><p><code>x</code>指定插值的源值。</p><p>描述:</p><p>当<code>edge0 &lt;x &lt;edge1</code>时，<code>smoothstep()</code>在0和1之间执行平滑的Hermite插值。这在需要具有平稳过渡的阈值函数的情况下很有用。 <code>smoothstep()</code>等效于：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs c">genType t;  <span class="hljs-comment">/* Or genDType t; */</span><br>t = clamp((x - edge0) / (edge1 - edge0), <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>);<br><span class="hljs-keyword">return</span> t * t * (<span class="hljs-number">3.0</span> - <span class="hljs-number">2.0</span> * t);<br></code></pre></td></tr></table></figure><p>如果<code>edge0≥edge1</code>，则结果是<code>undefined</code>。</p><h2 id="几何函数"><a href="#几何函数" class="headerlink" title="几何函数"></a>几何函数</h2><h3 id="length"><a href="#length" class="headerlink" title="length()"></a>length()</h3><h3 id="distance"><a href="#distance" class="headerlink" title="distance()"></a>distance()</h3><h3 id="dot"><a href="#dot" class="headerlink" title="dot()"></a>dot()</h3><p>计算两个向量的点积</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">float</span> <span class="hljs-built_in">dot</span>(<span class="hljs-type">float</span> x, <span class="hljs-type">float</span> y)  <br><span class="hljs-type">float</span> <span class="hljs-built_in">dot</span>(<span class="hljs-type">vec2</span> x, <span class="hljs-type">vec2</span> y)  <br><span class="hljs-type">float</span> <span class="hljs-built_in">dot</span>(<span class="hljs-type">vec3</span> x, <span class="hljs-type">vec3</span> y)  <br><span class="hljs-type">float</span> <span class="hljs-built_in">dot</span>(<span class="hljs-type">vec4</span> x, <span class="hljs-type">vec4</span> y)<br></code></pre></td></tr></table></figure><p>参数:<code>x</code>指定两个向量中的第一个 ,<code>y</code>指定两个向量中的第二个</p><p>描述:<code>dot()</code>返回两个向量x和y的点积。 即<code>x[0]·y[0] + x[1]·y[1] + ...</code>如果x和y相同，则点积的平方根等于向量的长度。 输入参数可以是浮标量或浮标向量。 如果是浮标量，则点函数是微不足道的，并返回x和y的乘积。</p><h3 id="cross"><a href="#cross" class="headerlink" title="cross()"></a>cross()</h3><p>计算两个向量的叉积</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">vec3</span> <span class="hljs-built_in">cross</span>(<span class="hljs-type">vec3</span> x, <span class="hljs-type">vec3</span> y)<br></code></pre></td></tr></table></figure><p>参数:    <code>x</code>指定两个向量中的第一个,<code>y</code>指定两个向量中的第二个.</p><p>描述:</p><p><code>cross()</code>返回两个向量x和y的叉积。 输入参数只能是3分量浮点向量。 叉积等于向量长度乘以x和y之间（较小）角的正弦值的乘积。</p><h3 id="normalize"><a href="#normalize" class="headerlink" title="normalize()"></a>normalize()</h3><p>计算与输入向量相同方向的单位向量</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">float</span> <span class="hljs-built_in">normalize</span>(<span class="hljs-type">float</span> x)  <br><span class="hljs-type">vec2</span> <span class="hljs-built_in">normalize</span>(<span class="hljs-type">vec2</span> x)  <br><span class="hljs-type">vec3</span> <span class="hljs-built_in">normalize</span>(<span class="hljs-type">vec3</span> x)  <br><span class="hljs-type">vec4</span> <span class="hljs-built_in">normalize</span>(<span class="hljs-type">vec4</span> x)<br></code></pre></td></tr></table></figure><p><code>x</code>指定要归一化的向量。</p><p>描述:<code>normalize()</code>返回一个向量，向量的方向与其参数x相同，但长度为1。</p><h3 id="facefoward"><a href="#facefoward" class="headerlink" title="facefoward()"></a>facefoward()</h3><p>返回指向与另一个方向相同的向量</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">float</span> <span class="hljs-built_in">faceforward</span>(<span class="hljs-type">float</span> N, <span class="hljs-type">float</span> I, <span class="hljs-type">float</span> Nref) <br><span class="hljs-type">vec2</span> <span class="hljs-built_in">faceforward</span>(<span class="hljs-type">vec2</span> N, <span class="hljs-type">vec2</span> I, <span class="hljs-type">vec2</span> Nref) <br><span class="hljs-type">vec3</span> <span class="hljs-built_in">faceforward</span>(<span class="hljs-type">vec3</span> N, <span class="hljs-type">vec3</span> I, <span class="hljs-type">vec3</span> Nref) <br><span class="hljs-type">vec4</span> <span class="hljs-built_in">faceforward</span>(<span class="hljs-type">vec4</span> N, <span class="hljs-type">vec4</span> I, <span class="hljs-type">vec4</span> Nref)<br></code></pre></td></tr></table></figure><h3 id="reflect"><a href="#reflect" class="headerlink" title="reflect()"></a>reflect()</h3><h3 id="refract"><a href="#refract" class="headerlink" title="refract()"></a>refract()</h3><h2 id="矩阵函数"><a href="#矩阵函数" class="headerlink" title="矩阵函数"></a>矩阵函数</h2><h3 id="matrixCompMult"><a href="#matrixCompMult" class="headerlink" title="matrixCompMult()"></a>matrixCompMult()</h3><p>执行两个矩阵的按分量乘法</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">mat2</span> <span class="hljs-built_in">matrixCompMult</span>(<span class="hljs-type">mat2</span> x, <span class="hljs-type">mat2</span> y)  <br><span class="hljs-type">mat3</span> <span class="hljs-built_in">matrixCompMult</span>(<span class="hljs-type">mat3</span> x, <span class="hljs-type">mat3</span> y)  <br><span class="hljs-type">mat4</span> <span class="hljs-built_in">matrixCompMult</span>(<span class="hljs-type">mat4</span> x, <span class="hljs-type">mat4</span> y)<br></code></pre></td></tr></table></figure><p>参数:</p><p><code>x</code>指定第一个矩阵被乘数。</p><p> <code>y</code>指定第二个矩阵被乘数。</p><p>描述:</p><p><code>matrixCompMult()</code>对两个矩阵进行按分量乘法，生成结果矩阵，其中每个分量<code>result[i][j]</code>计算为<code>x[i][j]</code>和<code>y[i][j]</code>的标量积</p><h2 id="向量函数"><a href="#向量函数" class="headerlink" title="向量函数"></a>向量函数</h2><h3 id="lessThan"><a href="#lessThan" class="headerlink" title="lessThan()"></a>lessThan()</h3><h3 id="lessThanEqual"><a href="#lessThanEqual" class="headerlink" title="lessThanEqual()"></a>lessThanEqual()</h3><h3 id="greaterThan"><a href="#greaterThan" class="headerlink" title="greaterThan()"></a>greaterThan()</h3><h3 id="greaterThanEqual"><a href="#greaterThanEqual" class="headerlink" title="greaterThanEqual()"></a>greaterThanEqual()</h3><h3 id="equal"><a href="#equal" class="headerlink" title="equal()"></a>equal()</h3><h3 id="notEqual"><a href="#notEqual" class="headerlink" title="notEqual()"></a>notEqual()</h3><h3 id="any"><a href="#any" class="headerlink" title="any()"></a>any()</h3><h3 id="all"><a href="#all" class="headerlink" title="all()"></a>all()</h3><h3 id="not"><a href="#not" class="headerlink" title="not()"></a>not()</h3><h2 id="纹理查找函数"><a href="#纹理查找函数" class="headerlink" title="纹理查找函数"></a>纹理查找函数</h2><h3 id="texture2D"><a href="#texture2D" class="headerlink" title="texture2D()"></a>texture2D()</h3><h3 id="textureCube"><a href="#textureCube" class="headerlink" title="textureCube()"></a>textureCube()</h3>]]></content>
    
    
    <categories>
      
      <category>着色器</category>
      
    </categories>
    
    
    <tags>
      
      <tag>着色器之书</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>着色器之书10</title>
    <link href="/blog/posts/2c21d9e3.html"/>
    <url>/blog/posts/2c21d9e3.html</url>
    
    <content type="html"><![CDATA[<h1 id="生成设计"><a href="#生成设计" class="headerlink" title="生成设计"></a>生成设计</h1><p>在经历了众多的重复与秩序之后，笔者自然而然地开始着手创造一些混沌。</p><h2 id="随机"><a href="#随机" class="headerlink" title="随机"></a>随机</h2><p><a href="http://www.ryojiikeda.com/project/testpattern/#testpattern_live_set"><img src="/blog/posts/2c21d9e3/ryoji-ikeda.jpg" class="" title="Ryoji Ikeda"> </a></p><p>随机性是熵的最大表现。我们如何在看似可预测而且严苛的代码环境中生成随机性呢？</p><p>让我们从分析下面的函数着手:</p><div class="container" style="margin:0;padding:0">    <div class="simpleFunction" data="y = fract(sin(x)*1.0);"></div></div><p>上面的例子中，我们提取了sin函数其波形的分数部分。值域为<code>-1.0</code> 到 <code>1.0</code> 之间的<a href="../glossary/?search=sin"><code>sin()</code></a> 函数被取了小数点后的部分(这里实际是指模1))，返回<code>0.0</code> 到 <code>1.0</code> 间的正值。我们可以用这种效果通过把正弦函数打散成小片段来得到一些伪随机数。如何实现呢？通过在<a href="../glossary/?search=sin"><code>sin(x)</code></a>的值上乘以大些的数。点击上面的函数，在1后面加些0。</p><p>当你加到 <code>100000.0</code> （方程看起来是这样的：<code>y = fract(sin(x)*100000.0)</code> ），你再也区分不出sin波了。小数部分的粒度将sine的循环变成了伪随机的混沌。</p><h2 id="控制混沌"><a href="#控制混沌" class="headerlink" title="控制混沌"></a>控制混沌</h2><p>使用随机会很难；它不是太混沌难测就是有时又不够混乱。看看下面的图例。要实现这样的效果，我们像之前描述的那样应用用 <code>rand()</code> 函数。</p><p>细看，你可以看到 <a href="../glossary/?search=sin"><code>sin()</code></a> 在 <code>-1.5707</code> 和 <code>1.5707</code> 上有较大波动。我打赌你现在一定理解这是为什么——那就是sin取得最大值和最小值的地方。</p><p>如果你仔细观察随机分布，你会注意到相比边缘，中部更集中。</p><div class="container" style="margin:0;padding:0">    <div class="simpleFunction" data="y = rand(x);    //y = rand(x)*rand(x);    //y = sqrt(rand(x));    //y = pow(rand(x),5.);">    </div></div><p>不久前 <a href="https://pixelero.wordpress.com">Pixelero</a> 发表了一篇<a href="https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/">关于随机分布的有意思的文章</a>。 我添加了些前几张图所有的函数来供你试验，看看如何改变分布。取消函数的注释，看看发生什么变化。</p><p>如果你读下 <a href="https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/">Pixelero 的文章</a>，一定谨记我们用的 <code>rand()</code> 是确定性随机，也被称作是伪随机。这就意味着， 就 <code>rand(1.)</code> 为例，总会返回相同的值。<a href="https://pixelero.wordpress.com/2008/04/24/various-functions-and-various-distributions-with-mathrandom/">Pixelero</a> 用 ActionSript 函数做了些参考，<code>Math.random()</code>，一个非确定性随机；每次调用都返回不同的值。</p><h2 id="2D-随机"><a href="#2D-随机" class="headerlink" title="2D 随机"></a>2D 随机</h2><p>现在我们对随机有了深入的理解，是时候将它应用到二维，<code>x</code> 轴和 <code>y</code> 轴。为此我们需要将一个二维向量转化为一维浮点数。这里有几种不同的方法来实现，但 <a href="../glossary/?search=dot"><code>dot()</code></a> 函数在这个例子中尤其有用。它根据两个向量的方向返回一个 <code>0.0</code> 到 <code>1.0</code> 之间的值。</p><!-- <div class="codeAndCanvas" data="2d-random.frag"></div> --><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/10/2d-random.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>看下第13行和15行，注意我们如何将 <code>vec2 st</code> 和另一个二维向量 （ <code>vec2(12.9898,78.233)</code>）。</p><ul><li><p>试着改变14和15行的值。看看随机图案的变化，想想从中我们能学到什么。</p></li><li><p>好好用鼠标（<code>u_mouse</code>）和时间（<code>u_time</code>）调戏这个随机函数，更好的理解它如何工作。</p></li></ul><h2 id="使用混沌"><a href="#使用混沌" class="headerlink" title="使用混沌"></a>使用混沌</h2><p>二维的随机看起来是不是像电视的噪点？对组成图像来说，随机是个难用的原始素材。让我们来学着如何来利用它。</p><p>我们的第一步是在网格上的应用；用 <a href="../glossary/?search=floor"><code>floor()</code></a> 函数，我们将会产生一个单元整数列表。看下下面的代码，尤其是22行和23行。</p><!-- <div class="codeAndCanvas" data="2d-random-mosaic.frag"></div> --><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/10/2d-random-mosaic.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>在缩放空间10倍后（在21行），我们将坐标系统的整数和小数部分分离。我们对最后一步操作不陌生，因为我们曾经用这种方法来将空间细分成 <code>0.0</code> 到 <code>1.0</code> 的小单元。我们根据得到坐标的整数部分作为一个通用值来隔离一个区域的像素，让它看起来像个单独的单元。然后我们可以用这个通用值来为这个区域得到一个随机值。因为我们的随机函数是伪随机，在那个单元内的所有像素返回的随机值都是一个常量。</p><p>取消第29行保留我们坐标的小数部分，这样我们仍旧可以将其用作一个坐标系统，来在单元内部画图形。</p><p>结合这两个量 — 坐标的整数部分和小数部分 — 将使你可以结合变化和秩序。</p><p>看下这个著名的 <code>10 PRINT CHR$(205.5+RND(1)); : GOTO 10</code>迷宫生成器的GLSL代码块。</p><!-- <div class="codeAndCanvas" data="2d-random-truchet.frag"></div> --><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/10/2d-random-truchet.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>这里我用前一章的 <code>truchetPattern()</code> 函数根据单元产生的随机值来随机画一个方向的对角线。</p><p>你可以通过取消50到53行的代码块的注释得到其他有趣的图案，或者通过取消35和36行来得到图案的动画。</p><h2 id="掌握随机"><a href="#掌握随机" class="headerlink" title="掌握随机"></a>掌握随机</h2><p><a href="http://www.ryojiikeda.com/">Ryoji Ikeda</a>，日本电子作曲家、视觉艺术家，是运用随机的大师；他的作品是如此的富有感染力而难忘。他在音乐和视觉媒介中随机的运用，不再是混乱无序，反而以假乱真地折射出我们技术文化的复杂性。</p><!-- <iframe src="https://player.vimeo.com/video/76813693?title=0&byline=0&portrait=0" width="800" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe> --><p><a href="https://player.vimeo.com/video/76813693?title=0&amp;byline=0&amp;portrait=0">随机的运用</a></p><p>看看 <a href="http://www.ryojiikeda.com/">Ikeda</a> 的作品并试试看下面的练习：</p><ul><li>做按行随机移动的单元（以相反方向）。只显示亮一些的单元。让各行的速度随时间变化。<div class="container" style="margin:0;padding:0">  <a href="../edit.php#10/ikeda-00.frag">      <canvas id="custom" class="canvas" data-fragment-url="/blog/glsl/10/ikeda-00.frag"  style="width:100%;object-fit: contain;min-height:100px">      </canvas>  </a></div></li></ul><ul><li>同样地，让某几行以不同的速度和方向。用鼠标位置关联显示单元的阀值。</li></ul><div class="container" style="margin:0;padding:0">    <a href="../edit.php#10/ikeda-03.frag">        <canvas id="custom" class="canvas" data-fragment-url="/blog/glsl/10/ikeda-03.frag"  style="width:100%;object-fit: contain;min-height:100px">        </canvas>    </a></div><ul><li>创造其他有趣的效果。</li></ul><div class="container" style="margin:0;padding:0">    <a href="../edit.php#10/ikeda-04.frag">        <canvas id="custom" class="canvas" data-fragment-url="/blog/glsl/10/ikeda-04.frag"  style="width:100%;object-fit: contain;min-height:100px">        </canvas>    </a></div><p>完美地掌握随机之美是困难的，尤其是你想要让作品看起来很自然。随机仅仅是过于混乱了，真实生活中很少有东西看上去如此 <code>random()</code>。如果观察（玻璃床上）雨滴的肌理或是股票的曲线——这两个都挺随机的——但他们和本章开始的随机图案看起来不是同一对爹妈生的。原因？嗯，随机值之间没有什么相关性，而大多数自然图案（肌理）都对前一个状态有所记忆（基于前一个状态）。</p><p>下一章我们将学习噪声，一种光滑 和 <em>自然的</em> 创作计算机混沌的方式。</p>]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
    <tags>
      
      <tag>着色器之书</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>着色器之书09</title>
    <link href="/blog/posts/4ce65006.html"/>
    <url>/blog/posts/4ce65006.html</url>
    
    <content type="html"><![CDATA[<h2 id="Patterns-图案"><a href="#Patterns-图案" class="headerlink" title="Patterns 图案"></a>Patterns 图案</h2><p>因为着色器按一个个像素执行，那么无论你重复一个图形多少次，计算的数量仍然是个常数。</p><p><a href="../edit.php#09/dots5.frag"> <img src="/blog/posts/4ce65006/warmerdam.jpg" class="" title="Nina Warmerdam"> </a></p><p>本章中我们将综合我们目前所学的并应用在画布上。和前几章一样，我们的策略依然基于乘以空间坐标（0到1之间），这样我们的画的在0到1之间的图形就会重复地形成网格。</p><p><em>“网格提供一种基于人的直觉发明事物的框架，并且可以颠覆。自然的混沌肌理提供一种对比和秩序的迹象。从早期罗马浴场里的陶瓷图案到几何镶嵌，人们那时候就习惯用网格来点缀他们的生活。”</em><a href="http://10print.org/"><em>10 PRINT</em>, Mit Press, (2013)</a></p><p>首先让我们记住 <a href="../glossary/?search=fract"><code>fract()</code></a> 函数。它返回一个数的分数部分，本质上是除1的余数（<a href="../glossary/?search=mod"><code>mod(x,1.0)</code></a>）。换句话说， <a href="../glossary/?search=fract"><code>fract()</code></a> 返回小数点后的数。 我们单位化的坐标系变量 (<code>st</code>) 已经是 0.0 到 1.0 之间的了。所以像下面这么做并没有必要：</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">void</span> main()&#123;<br><span class="hljs-type">vec2</span> st = <span class="hljs-built_in">gl_FragCoord</span>.xy/u_resolution;<br><span class="hljs-type">vec3</span> color = <span class="hljs-type">vec3</span>(<span class="hljs-number">0.0</span>);<br>    st = <span class="hljs-built_in">fract</span>(st);<br>color = <span class="hljs-type">vec3</span>(st,<span class="hljs-number">0.0</span>);<br><span class="hljs-built_in">gl_FragColor</span> = <span class="hljs-type">vec4</span>(color,<span class="hljs-number">1.0</span>);<br>&#125;<br></code></pre></td></tr></table></figure><p>但如果我们放大单位化坐标系 — 比如说3倍 — 我们会得到三组 0 到 1 的线性插值的数列：第一组在 0-1 之间，第二组浮点数在 1-2 之间以及第三组在 2-3 之间的浮点数。</p><!-- <div class="codeAndCanvas" data="grid-making.frag"></div> --><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/grid-making.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>现在是时候在子空间（网格单元的空间）里画点什么了。取消27行的注释。（因为我们等比例放大了x和y坐标，所以不会改变坐标的比例，图形会和预期的一样。）</p><p>试试下面的练习来深入理解：</p><ul><li><p>把空间乘以不同的数。试试用浮点数，还有分别给x和y不同的系数。</p></li><li><p>把这个平铺技巧做成一个可以反复使用的函数。</p></li><li><p>把画布分成 3 行 3 列。 指出如何定义行和列的线程的，并用这种方式改变显示着的图形。试着做一个井字棋。</p></li></ul><h3 id="在图案内部应用矩阵"><a href="#在图案内部应用矩阵" class="headerlink" title="在图案内部应用矩阵"></a>在图案内部应用矩阵</h3><p>鉴于每个细分或者说单元都是我们正在使用的单位化坐标系的小单元，我们可以对每个内部空间施以矩阵变换来平移，旋转和缩放。</p><!-- <div class="codeAndCanvas" data="checks.frag"></div> --><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/checks.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><ul><li><p>想想怎么让这些图案有趣的动起来。考虑颜色，形状，运动的变换。做三种动画。</p></li><li><p>通过组合不同的形状重新创造更复杂的图案。</p></li></ul><div class="container" style="margin:0;padding:0">    <a href="https://www.gishai.top/glsl/#/diamondtiles">        <canvas id="custom" class="canvas" data-fragment-url="/blog/glsl/diamondtiles.frag"  style="width:100%;object-fit: contain;min-height:100px">        </canvas>    </a></div><ul><li>结合多层图案来制作你自己的 <a href="https://www.google.com/search?q=scottish+patterns+fabric&amp;tbm=isch&amp;tbo=u&amp;source=univ&amp;sa=X&amp;ei=Y1aFVfmfD9P-yQTLuYCIDA&amp;ved=0CB4QsAQ&amp;biw=1399&amp;bih=799#tbm=isch&amp;q=Scottish+Tartans+Patterns">Scottish Tartan Patterns</a>.</li></ul><p><a href="http://graphicriver.net/item/vector-pattern-scottish-tartan/6590076"> <img src="/blog/posts/4ce65006/tartan-1599051168043.jpg" class="" title="Vector Pattern Scottish Tartan By Kavalenkava"> </a></p><h3 id="偏移图案"><a href="#偏移图案" class="headerlink" title="偏移图案"></a>偏移图案</h3><p>So let’s say we want to imitate a brick wall. Looking at the wall, you can see a half brick offset on x in every other row. How we can do that?</p><p>假如我们想要模仿砖墙。看，下面的墙，你是不是看到一半的砖在x方向上偏移了一半砖的长度，没隔一行偏移一次。我们如何实现？</p><img src="/blog/posts/4ce65006/brick-1599051171980.jpg" class=""><p>第一步我们需要知道某行的线程是奇数还是偶数，以为我们可以通过奇偶来决定是否要在x方向上偏移那一行。</p><p><strong><strong>我们需要两段来解决这个问题</strong></strong></p><p>要判断我们的线程是一个奇数行或者偶数行，我们要用 <code>2.0</code> 的 <a href="../glossary/?search=mod"><code>mod()</code></a> 。 然后根据结果是否大于 <code>1.0</code> 来判断。看一下下面的函数，取消最后两行的注释。</p><div class="simpleFunction" data="y = mod(x,2.0);// y = mod(x,2.0) < 1.0 ? 0. : 1. ;// y = step(1.0,mod(x,2.0));"></div><p>正如你所见，我们可以用一个 <a href="https://en.wikipedia.org/wiki/%3F:">三元算符号</a> （第二行）来检查 <code>2.0</code> 的<a href="../glossary/?search=mod"><code>mod()</code></a>（余数）小于 <code>1.0</code> 或者类似地，我们用 <a href="../glossary/?search=step"><code>step()</code></a>  函数做相同的操作，但（其实）更快。为什么呢？ 因为虽然要知道每个显卡如何优化和编译代码并不容易，但是可以安全地假设内置函数总比非内置的函数快。任何时候你都以调用内置函数，干嘛不用呢！</p><p>现在我们有这些找出奇数的方程，这样我们就可以给奇数行一个偏移量，然后就可以把 <em>砖块</em> 做出拼砖的效果。下面代码的第14行便是我们用来“侦测”奇数行，并予之半个单位在x上的偏移的。注意到对偶数行，函数的返回值是 <code>0.0</code>， <code>0.0</code> 乘以 <code>0.5</code> 得到一个 <code>0.0</code> 的偏移。 但是奇数行我们用函数的返回值， <code>1.0</code>， 乘以偏移量 <code>0.5</code>，这样便向坐标系的 <code>x</code> 轴偏移了 <code>0.5</code>。  </p><p>现在试着取消32行的注释 — 拉伸长宽比来模仿“现代砖块”的长宽比。通过取消第40行的的代码，你可以注意到坐标系统是如何看起来映射到红绿色的。</p><!-- <div class="codeAndCanvas" data="bricks.frag"></div> --><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/bricks.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><ul><li><p>试着根据时间变化对偏移量做动画。</p></li><li><p>另做一个动画，让偶数行向左移，奇数行向右移动。</p></li><li><p>能不能根据列重复这样的效果？</p></li><li><p>试着结合 <code>x</code> 和 <code>y</code> 轴的偏移来得到下面这样的效果：</p></li></ul><div class="container" style="margin:0;padding:0">    <a href="https://www.gishai.top/glsl/#/marching_dots">        <canvas id="custom" class="canvas" data-fragment-url="/blog/glsl/marching_dots.frag"  style="width:100%;object-fit: contain;min-height:100px">        </canvas>    </a></div><h2 id="Truchet-瓷砖"><a href="#Truchet-瓷砖" class="headerlink" title="Truchet 瓷砖"></a>Truchet 瓷砖</h2><p>目前我们学了如何区分奇数行/列或偶数行/列，（类似的），（我们也）可能再用（这个技巧）根据位置来设计元素。 考虑到 <a href="http://en.wikipedia.org/wiki/Truchet_tiles">Truchet Tiles</a> 的例子，即一个单一设计元素可以以四种不同的方式呈现：</p><img src="/blog/posts/4ce65006/truchet-00-1599051176947.png" class=""><p>通过改变对角瓷砖的图案，便可能组成无限种复杂设计的可能。</p><img src="/blog/posts/4ce65006/truchet-01-1599051178978.png" class=""><p>仔细观察 <code>rotateTilePattern()</code> 函数, 它把坐标空间细分成四个单元并赋予每一个旋转值。</p><!-- <div class="codeAndCanvas" data="truchet.frag"></div> --><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/truchet.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><ul><li><p>注释，取消注释，以及复制第69到72行来创作新的设计。</p></li><li><p>把黑白三角变成其他元素，例如：半圆，旋转的方形或直线。</p></li><li><p>编写根据元素自身位置旋转的图形代码。</p></li><li><p>创作一个根据其位置改变其他属性的图案。</p></li><li><p>想想其他能用这章原理的案例，不一定是图案. (例: 易经卦)</p></li></ul><div class="container" style="margin:0;padding:0">    <a href="https://www.gishai.top/glsl/#/iching">        <canvas id="custom" class="canvas" data-fragment-url="/blog/glsl/iching-01.frag"  style="width:100%;object-fit: contain;min-height:100px">        </canvas>    </a></div><h2 id="制定自己的规则"><a href="#制定自己的规则" class="headerlink" title="制定自己的规则"></a>制定自己的规则</h2><p>制作程序图案是种寻找最小可重复元素的古老练习（灵修）。我们作为长时间使用网格和图案来装饰织物、地面和物品的镶边物种：从古希腊的弯曲图案，到中国的窗栅设计，重复和变化的愉悦吸引我们的想象。花些时间浏览 <a href="https://archive.org/stream/traditionalmetho00chririch#page/130/mode/2up">decorative</a> <a href="https://www.pinterest.com/patriciogonzv/paterns/">patterns</a> 并看看在漫长的历史里，艺术家和设计师是如何寻找在秩序的预测性和（由）混沌和衍变（产生）的惊奇之间的边界的。从阿拉伯几何图案，到斑斓的非洲编制艺术，这里有一整个图案的宇宙要学习。</p><img src="/blog/posts/4ce65006/geometricpatters.png" class="" title="Franz Sales Meyer"><p>这章我们就结束算法绘图部分了。在接下来的章节，我们会学习如何把熵（其实就是混乱和随机的意思）加入到我们的着色器中来产生生成设计。</p>]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
    <tags>
      
      <tag>着色器之书</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>着色器之书08</title>
    <link href="/blog/posts/3be16090.html"/>
    <url>/blog/posts/3be16090.html</url>
    
    <content type="html"><![CDATA[<h2 id="2D-Matrices-二维矩阵"><a href="#2D-Matrices-二维矩阵" class="headerlink" title="2D Matrices 二维矩阵"></a>2D Matrices 二维矩阵</h2><div class="container" style="margin:0;padding:0">    <canvas id="custom" class="canvas" data-fragment-url="/blog/glsl/matrix.frag"  style="width:100%;object-fit: contain;min-height:100px">    </canvas></div><!-- <div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/matrix.frag" style="width:100%;height:200px;margin-bottom:10px"></div></div> --><h3 id="平移"><a href="#平移" class="headerlink" title="平移"></a>平移</h3><p>之前的章节我们学习了如何制作一些图形 - 而如何移动它们的技巧则是借助移动它们自身的参考坐标系。我们只需要给 <code>st</code> 变量加上一个包含每个片段的位置的向量。这样就移动了整个坐标系。</p><img src="/blog/posts/3be16090/translate-1599051067561.jpg" class=""><p>还是画着比较更容易解释，如上图所示：</p><ul><li>取消下面代码中第35行的注释，看下坐标空间是如何平移的。</li></ul><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/cross-translate.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>现在尝试下下面的练习：</p><ul><li>结合 <code>u_time</code> 和造型函数来移动十字，并试着让它有趣一点。找一个你觉得你感兴趣的某种运动形式，让这个十字也这样运动。记录“真实世界”的一些现象或许对你有所启发 — 可以是波的运动，摆动，弹球，汽车的加速运动，一辆自行车的刹车。</li></ul><h3 id="旋转"><a href="#旋转" class="headerlink" title="旋转"></a>旋转</h3><p>要移动物体，我们同样需要移动整个空间（坐标）系统。为此我们将使用一个<a href="http://en.wikipedia.org/wiki/Matrix_%28mathematics%29">矩阵</a>。矩阵是一个通过行和列定义的一组数。用矩阵乘以一个向量是用一组精确的规则定义的，这样做是为了以一组特定的方式来改变向量的值。</p><p><a href="https://en.wikipedia.org/wiki/Matrix"><img src="%E7%9D%80%E8%89%B2%E5%99%A8%E4%B9%8B%E4%B9%A608/matrixes.png" alt="Wikipedia entry for Matrix"></a></p><p>GLSL本身支持2维，3维和4维方阵（m<em>m矩阵）：<a href="../glossary/?search=mat2"><code>mat2</code></a> (2x2), <a href="../glossary/?search=mat3"><code>mat3</code></a> (3x3) 和 <a href="../glossary/?search=mat4"><code>mat4</code></a> (4x4)。GLSL同样支持矩阵相乘 (```</em><code>)和特殊矩阵函数([</code>matrixCompMult()```](../glossary/?search=matrixCompMult))。</p><p>基于矩阵的特性，我们便有可能构造一个矩阵来产生特定的作用。比如我们可以用一个矩阵来平移一个向量：</p><img src="/blog/posts/3be16090/3dtransmat-1599051112644.png" class=""><p>更有趣的是，我们可以用矩阵来旋转坐标系统：</p><img src="/blog/posts/3be16090/rotmat-1599051114875.png" class=""><p>看下下面构成2维旋转的矩阵的代码。这个函数根据上面的<a href="http://en.wikipedia.org/wiki/Rotation_matrix">公式</a>，将二维向量绕 <code>vec2(0.0)</code> 点旋转。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">mat2</span> rotate2d(<span class="hljs-type">float</span> _angle)&#123;<br>    <span class="hljs-keyword">return</span> <span class="hljs-type">mat2</span>(<span class="hljs-built_in">cos</span>(_angle),-<span class="hljs-built_in">sin</span>(_angle),<br>                <span class="hljs-built_in">sin</span>(_angle),<span class="hljs-built_in">cos</span>(_angle));<br>&#125;<br></code></pre></td></tr></table></figure><p>根据以往我们画形状的方式，这并不是我们想要的。我们的十字是画在画布中心的，对应于点 <code>vec2(0.5)</code> 。所以，再旋转坐标空间之前，我们需要先把图形移到中心点，坐标 <code>vec2(0.0)</code> ，再旋转坐标空间，最后在移动回原点。</p><img src="/blog/posts/3be16090/rotate-1599051119075.jpg" class=""><p>就像下面的代码：</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/cross-rotate.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>试试下面的练习：</p><ul><li><p>取消第45行的代码，看看会发生什么。</p></li><li><p>在37行和39行，将旋转之前的平移注释掉，观察结果。</p></li><li><p>用旋转改进在平移练习中模拟的动画。</p></li></ul><h3 id="缩放"><a href="#缩放" class="headerlink" title="缩放"></a>缩放</h3><p>我们看到了如何用矩阵平移和旋转物体。（或者更准确的说，如何通过变换坐标系统来旋转和移动物体。）如果你用过3D建模软件或者 Processing中的 pushmatrix 和 popmatrix 函数，你会知道矩阵也可以被用来缩放物体的大小。</p><img src="/blog/posts/3be16090/scale-1599051122829.png" class=""><p>根据上面的公式，我们知道如何构造一个2D缩放矩阵：</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">mat2</span> scale(<span class="hljs-type">vec2</span> _scale)&#123;<br>    <span class="hljs-keyword">return</span> <span class="hljs-type">mat2</span>(_scale.x,<span class="hljs-number">0.0</span>,<br>                <span class="hljs-number">0.0</span>,_scale.y);<br>&#125;<br></code></pre></td></tr></table></figure><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/cross-scale.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>试试下面的练习，尝试深入理解矩阵的工作机制：</p><ul><li><p>取消上面代码中的第42行来观察空间坐标是如何被缩放的。</p></li><li><p>看看注释掉37和39行，变换之前和之后的缩放，会发生什么。</p></li><li><p>试着结合旋转矩阵和缩放矩阵。注意他们的先后顺序。先乘以一个矩阵，再乘以向量。</p></li><li><p>现在你知道如何画不同的图形，知道如何移动，旋转和缩放它们，是时候用这些来创作了。设计一个<a href="https://www.pinterest.com/patriciogonzv/huds/">fake UI or HUD (heads up display)</a>。参考<a href="https://www.shadertoy.com/user/ndel">Ndel</a>在ShaderToy上的例子。</p></li></ul><!-- <iframe width="800" height="450" frameborder="0" src="https://www.shadertoy.com/embed/4s2SRt?gui=true&t=10&paused=true" allowfullscreen></iframe> --><p> <a href="https://www.shadertoy.com/embed/4s2SRt?gui=true&amp;t=10&amp;paused=true">https://www.shadertoy.com</a></p><h3 id="Other-uses-for-matrices-YUV-color-矩阵的其他应用：YUV-颜色"><a href="#Other-uses-for-matrices-YUV-color-矩阵的其他应用：YUV-颜色" class="headerlink" title="Other uses for matrices: YUV color 矩阵的其他应用：YUV 颜色"></a>Other uses for matrices: YUV color 矩阵的其他应用：YUV 颜色</h3><p><a href="http://en.wikipedia.org/wiki/YUV">YUV</a> 是个用来模拟照片和视频的编码的色彩空间。这个色彩空间考虑人类的感知，减少色度的带宽。</p><p>下面的代码展现一种利用GLSL中的矩阵操作来切换颜色模式的有趣可能。</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/yuv.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>正如你所见，我们用对向量乘以矩阵的方式对待色彩。用这种方式，我们“移动”这些值。</p><p>这章我们学习如何运用矩阵变换来移动，旋转和缩放向量。除了之前章节学的图形，这些变换是创作的基础。在接下来的章节我们会应用我们所学的制作漂亮的程序纹理。你会发现编程的重复性和多样性是种令人兴奋的实践。</p>]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
    <tags>
      
      <tag>着色器之书</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>着色器之书07</title>
    <link href="/blog/posts/ab5e7d01.html"/>
    <url>/blog/posts/ab5e7d01.html</url>
    
    <content type="html"><![CDATA[<h2 id="形状"><a href="#形状" class="headerlink" title="形状"></a>形状</h2><img src="/blog/posts/ab5e7d01/froebel-1599050937888.jpg" class="" title="Alice Hubbard, Providence, United States, ca. 1892. Photo: Zindman&#x2F;Freemont."><p>终于！我们一直学习的技能就等着这一刻！你已经学习过GLSL的大部分基础，类型和函数。你一遍又一遍的练习你的造型方程。是时候把他们整合起来了。你就是为了这个挑战而来的！在这一章里，你会学习到如何以一种并行处理方式来画简单的图形。</p><h3 id="长方形"><a href="#长方形" class="headerlink" title="长方形"></a>长方形</h3><p>想象我们有张数学课上使用的方格纸，而我们的作业是画一个正方形。纸的大小是10 x 10而正方形应该是8 x 8。你会怎么做？</p><img src="/blog/posts/ab5e7d01/grid_paper-1599050934483.jpg" class=""><p>你是不是会涂满除了第一行第一列和最后一行和最后一列的所有格点？</p><p>这和着色器有什么关系？方格纸上的每个小方形格点就是一个线程（一个像素）。每个格点有它的位置，就想棋盘上的坐标一样。在之前的章节我们将x和y映射到rgb通道，并且我们学习了如何将二维边界限制在0和1之间。我们如何用这些来画一个中心点位于屏幕中心的正方形？</p><p>我们从空间角度来判别的 if 语句伪代码开始。这个原理和我们思考方格纸的策略异曲同工。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-keyword">if</span> ( (X GREATER THAN <span class="hljs-number">1</span>) AND (Y GREATER THAN <span class="hljs-number">1</span>) )<br>    paint white<br><span class="hljs-keyword">else</span><br>    paint black<br></code></pre></td></tr></table></figure><p>现在我们有个更好的主意让这个想法实现，来试试把if语句换成step（），并用0到1代替10 x 10的范围。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-keyword">uniform</span> <span class="hljs-type">vec2</span> u_resolution;<br><br><span class="hljs-type">void</span> main()&#123;<br>    <span class="hljs-type">vec2</span> st = <span class="hljs-built_in">gl_FragCoord</span>.xy/u_resolution.xy;<br>    <span class="hljs-type">vec3</span> color = <span class="hljs-type">vec3</span>(<span class="hljs-number">0.0</span>);<br><br>    <span class="hljs-comment">// Each result will return 1.0 (white) or 0.0 (black).</span><br>    <span class="hljs-type">float</span> left = <span class="hljs-built_in">step</span>(<span class="hljs-number">0.1</span>,st.x);   <span class="hljs-comment">// Similar to ( X greater than 0.1 )</span><br>    <span class="hljs-type">float</span> bottom = <span class="hljs-built_in">step</span>(<span class="hljs-number">0.1</span>,st.y); <span class="hljs-comment">// Similar to ( Y greater than 0.1 )</span><br><br>    <span class="hljs-comment">// The multiplication of left*bottom will be similar to the logical AND.</span><br>    color = <span class="hljs-type">vec3</span>( left * bottom );<br><br>    <span class="hljs-built_in">gl_FragColor</span> = <span class="hljs-type">vec4</span>(color,<span class="hljs-number">1.0</span>);<br>&#125;<br></code></pre></td></tr></table></figure><p>step（）函数会让没每一个小于0.1的像素变成黑色（vec3（0.0））并将其余的变成白色（vec3（1.0））。<code>left</code> 乘 <code>bottom</code> 效果相当于逻辑 AND —— 当 x y 都为 1.0 时乘积才能是 1.0。这样做的效果就是画了两条黑线，一个在画布的底边另一个在左边。</p><img src="/blog/posts/ab5e7d01/rect-01-1599050929972.jpg" class=""><p>在前一例代码中我们重复每个像素的结构（左边和底边）。我们可以把原来的一个值换成两个值直接给step（）来精减代码。就像这样：</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">vec2</span> borders = <span class="hljs-built_in">step</span>(<span class="hljs-type">vec2</span>(<span class="hljs-number">0.1</span>),st);<br><span class="hljs-type">float</span> pct = borders.x * borders.y;<br></code></pre></td></tr></table></figure><p>目前为止，我们只画了长方形的两条边（左边和底面）。看下下面的例子：</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/rect-making.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>取消 21~22 行的注释来看看如何转置坐标的同时重复使用 <code>step()</code> 函数。这样二维向量 vec2(0.0,0.0) 会被变换到右上角。这就是转置页面和重复过程的数字等价。</p><img src="/blog/posts/ab5e7d01/rect-02-1599050926660.jpg" class=""><p>注意在 18 行和 22 行，所有的边（宽）都被放大了。等价于这样写：</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">vec2</span> bl = <span class="hljs-built_in">step</span>(<span class="hljs-type">vec2</span>(<span class="hljs-number">0.1</span>),st);       <span class="hljs-comment">// bottom-left</span><br><span class="hljs-type">vec2</span> tr = <span class="hljs-built_in">step</span>(<span class="hljs-type">vec2</span>(<span class="hljs-number">0.1</span>),<span class="hljs-number">1.0</span>-st);   <span class="hljs-comment">// top-right</span><br>color = <span class="hljs-type">vec3</span>(bl.x * bl.y * tr.x * tr.y);<br></code></pre></td></tr></table></figure><p>是不是很有趣？这种都是关于运用 step() 函数、逻辑运算和转置坐标的结合。</p><p>再进行下一个环节之前，挑战下下面的练习：</p><ul><li><p>改变长方形的比例和大小。</p></li><li><p>用 smoothstep() 函数代替 step() 函数，试试在相同的代码下会有什么不同。注意通过改变取值，你可以不仅可以得到模糊边界也可以由漂亮的顺滑边界。</p></li><li><p>应用 floor() 做个另外的案例。</p></li><li><p>挑个你最喜欢的做成函数，这样未来你可以调用它。并且让它灵活高效。</p></li><li><p>写一个只画长方形四边的函数。</p></li><li><p>想一下如何在一个画板上移动并放置不同的长方形？如果你做出来了，试着像<a href="http://en.wikipedia.org/wiki/Piet_Mondrian">Piet Mondrian</a>一样创作以长方形和色彩的图画。</p></li></ul><img src="/blog/posts/ab5e7d01/mondrian.jpg" class="" title="Piet Mondria"><h3 id="圆"><a href="#圆" class="headerlink" title="圆"></a>圆</h3><p>在笛卡尔坐标系下，用方格纸来画正方形和长方形是很容易的。但是画圆就需要另一种方式了，尤其我们需要一个对“每个像素”的算法。一种解决办法是用<a href="../glossary/?search=step"><code>step()</code></a>函数将重新映射的空间坐标来画圆。</p><p>如何实现？让我们重新回顾一下数学课上的方格纸：我们把圆规展开到半径的长度，把一个针脚戳在圆圆心上，旋转着把圆的边界留下来。</p><img src="/blog/posts/ab5e7d01/compass-1599050919467.jpg" class=""><p>将这个过程翻译给 shader 意味着纸上的每个方形格点都会隐含着问每个像素（线程）是否在圆的区域以内。我们通过计算像素到中心的距离来实现（这个判断）。</p><img src="/blog/posts/ab5e7d01/circle-1599050916035.jpg" class=""><p>There are several ways to calculate that distance. The easiest one uses the <a href="../glossary/?search=distance"><code>distance()</code></a> function, which internally computes the <a href="../glossary/?search=length"><code>length()</code></a> of the difference between two points (in our case the pixel coordinate and the center of the canvas). The <code>length()</code> function is nothing but a shortcut of the <a href="http://en.wikipedia.org/wiki/Hypotenuse">hypotenuse equation</a> that uses square root (<a href="../glossary/?search=sqrt"><code>sqrt()</code></a>) internally.</p><p>有几种方法来计算距离。最简单的是用<a href="../glossary/?search=distance"><code>distance()</code></a>函数，这个函数其实内部调用 <a href="../glossary/?search=length"><code>length()</code></a>函数，计算不同两点的距离（在此例中是像素坐标和画布中心的距离）。length（）函数内部只不过是用平方根(<a href="../glossary/?search=sqrt"><code>sqrt()</code></a>)计算斜边的方程。</p><img src="/blog/posts/ab5e7d01/hypotenuse-1599050911156.png" class=""><p>你可以使用<a href="../glossary/?search=distance"><code>distance()</code></a>, <a href="../glossary/?search=length"><code>length()</code></a> 或 <a href="../glossary/?search=sqrt"><code>sqrt()</code></a>到计算屏幕的中心的距离。下面的代码包含着三个函数，毫无悬念的他们返回相同的结果。</p><ul><li>注释和取消某行的注释来试试看用不同方式得到相同的结果。</li></ul><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/circle-making.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>上回我们把到中心的距离映射为颜色亮度。离中心越近的越暗。注意到映射值不宜过高，因为从中心（vec2(0.5, 0.5)）到最远距离才刚刚超过0.5一点。仔细考察这个映射：</p><ul><li><p>你能从中推断出什么？</p></li><li><p>我们怎么用这个方法来画圆？</p></li><li><p>试试有没有其他方法来实现这样画布内圆形渐变的效果。</p></li></ul><h3 id="距离场"><a href="#距离场" class="headerlink" title="距离场"></a>距离场</h3><p>我们可也可以从另外的角度思考上面的例子：把它当做海拔地图（等高线图）——越黑的地方意味着海拔越高。想象下，你就在圆锥的顶端，那么这里的渐变就和圆锥的等高线图有些相似。到圆锥的水平距离是一个常数0.5。这个距离值在每个方向上都是相等的。通过选择从那里截取这个圆锥，你就会得到或大或小的圆纹面。</p><img src="/blog/posts/ab5e7d01/distance-field-1599051008474.jpg" class=""><p>其实我们是通过“空间距离”来重新解释什么是图形。这种技巧被称之为“距离场”，从字体轮廓到3D图形被广泛应用。</p><p>来小试下牛刀：</p><ul><li><p>用<a href="../glossary/?search=step"><code>step()</code></a>函数把所有大于0.5的像素点变成白色，并把小于的变成黑色（0.0）。</p></li><li><p>反转前景色和背景色。</p></li><li><p>调戏下<a href="../glossary/?search=smoothstep"><code>smoothstep()</code></a>函数，用不同的值来试着做出一个边界顺滑的圆。</p></li><li><p>一旦遇到令你满意的应用，把他写成一个函数，这样将来就可以调用了。</p></li><li><p>给这个圆来些缤纷的颜色吧！</p></li><li><p>再加点动画？一闪一闪亮晶晶？或者是砰砰跳动的心脏？（或许你可以从上一章汲取一些灵感）</p></li><li><p>让它动起来？能不能移动它并且在同一个屏幕上放置多个圆？</p></li><li><p>如果你结合函数来混合不同的距离场，会发生什么呢？</p></li></ul><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs glsl">pct = <span class="hljs-built_in">distance</span>(st,<span class="hljs-type">vec2</span>(<span class="hljs-number">0.4</span>)) + <span class="hljs-built_in">distance</span>(st,<span class="hljs-type">vec2</span>(<span class="hljs-number">0.6</span>));<br>pct = <span class="hljs-built_in">distance</span>(st,<span class="hljs-type">vec2</span>(<span class="hljs-number">0.4</span>)) * <span class="hljs-built_in">distance</span>(st,<span class="hljs-type">vec2</span>(<span class="hljs-number">0.6</span>));<br>pct = <span class="hljs-built_in">min</span>(<span class="hljs-built_in">distance</span>(st,<span class="hljs-type">vec2</span>(<span class="hljs-number">0.4</span>)),<span class="hljs-built_in">distance</span>(st,<span class="hljs-type">vec2</span>(<span class="hljs-number">0.6</span>)));<br>pct = <span class="hljs-built_in">max</span>(<span class="hljs-built_in">distance</span>(st,<span class="hljs-type">vec2</span>(<span class="hljs-number">0.4</span>)),<span class="hljs-built_in">distance</span>(st,<span class="hljs-type">vec2</span>(<span class="hljs-number">0.6</span>)));<br>pct = <span class="hljs-built_in">pow</span>(<span class="hljs-built_in">distance</span>(st,<span class="hljs-type">vec2</span>(<span class="hljs-number">0.4</span>)),<span class="hljs-built_in">distance</span>(st,<span class="hljs-type">vec2</span>(<span class="hljs-number">0.6</span>)));<br></code></pre></td></tr></table></figure><ul><li>用这种技巧制作三个元素，如果它们是运动的，那就再好不过啦！</li></ul><h4 id="添加自己的工具箱"><a href="#添加自己的工具箱" class="headerlink" title="添加自己的工具箱"></a>添加自己的工具箱</h4><p>就计算效率而言，<a href="../glossary/?search=sqrt"><code>sqrt()</code></a>函数，以及所有依赖它的运算，都耗时耗力。<a href="../glossary/?search=dot"><code>dot()</code></a>点乘是另外一种用来高效计算圆形距离场的方式。</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/circle.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><h3 id="距离场的特点"><a href="#距离场的特点" class="headerlink" title="距离场的特点"></a>距离场的特点</h3><img src="/blog/posts/ab5e7d01/zen-garden-1599051013564.jpg" class="" title="Zen garden"><p>距离场几乎可以用来画任何东西。显然，图形越复杂，方程也越复杂。但是一旦你找到某个特定图形的公式，就很容易添加图形或应用像过渡边界的效果。正因如此，距离场经常用于字体渲染，例如<a href="https://www.mapbox.com/blog/text-signed-distance-fields/">Mapbox GL Labels</a>, <a href="https://twitter.com/mattdesl">Matt DesLauriers</a> <a href="http://mattdesl.svbtle.com/material-design-on-the-gpu">Material Design Fonts</a> 和 <a href="http://chimera.labs.oreilly.com/books/1234000001814/ch07.html#ch07_id36000921">as is describe on Chapter 7 of iPhone 3D Programming, O’Reilly</a>.</p><p>看看下面的代码：</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/rect-df.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>我们一开始把坐标系移到中心并把它映射到-1到1之间。在 <em>24行</em> 这儿，我们用一个<a href="../glossary/?search=fract"><code>fract()</code></a> 函数来呈现这个距离场产生的图案。这个距离场不断重复，就像在禅花园看到的环一样。</p><p>现在我们来看下 <em>19行</em> 的距离场方程。这里我们在计算点  <code>(.3,.3)</code> 或 <code>vec3(.3)</code>到所有四象限的距离（这就是 <a href="../glossary/?search=abs"><code>abs()</code></a> 在起作用）。</p><p>如果你取消第 <em>20行</em> 的注释，你会发现我们把到四个点的距离用<a href="../glossary/?search=min"><code>min()</code></a> 函数合并到0，并产生了一个有趣的图案。</p><p>现在再试着取消第 <em>21行</em> 的注释，我们做的和之前一样，只不过这次用的是 <a href="../glossary/?search=max"><code>max()</code></a> 函数。这次的记过是圆角矩形。注意距离场的环形是如何离中心越远越光滑的。</p><p>最后从<em>27 行到 29 行</em>一行行地取消注释，思考距离场的不同用途。</p><h3 id="极坐标下的图形"><a href="#极坐标下的图形" class="headerlink" title="极坐标下的图形"></a>极坐标下的图形</h3><img src="/blog/posts/ab5e7d01/mangold.jpg" class="" title="Robert Mangold"><p>在关于颜色的章节我们通过如下的方程把每个像素的 <strong>半径</strong> 和 <strong>角度</strong> 笛卡尔坐标映射到极坐标。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">vec2</span> pos = <span class="hljs-type">vec2</span>(<span class="hljs-number">0.5</span>)-st;<br><span class="hljs-type">float</span> r = <span class="hljs-built_in">length</span>(pos)*<span class="hljs-number">2.0</span>;<br><span class="hljs-type">float</span> a = <span class="hljs-built_in">atan</span>(pos.y,pos.x);<br></code></pre></td></tr></table></figure><p>我们用了部分方程在这章的开头来画圆，即用 <a href="../glossary/?search=length"><code>length()</code></a> 计算到中心的距离。现在我们可以用极坐标来画圆。</p><p>极坐标这种方式虽然有所限制但却十分简单。</p><p>下面你会看到在同样在笛卡尔坐标下图形在极坐标下的着色器案例（在 <em>lines 21 和 25</em>之间）。对这些函数一个个取消注释，看看两坐标系之间的联系。</p><div class="container" style="margin:0;padding:0">    <div class="simpleFunction" data="y = cos(x*3.);    //y = abs(cos(x*3.));    //y = abs(cos(x*2.5))*0.5+0.3;    //y = abs(cos(x*12.)*sin(x*3.))*.8+.1;    //y = smoothstep(-.5,1., cos(x*10.))*0.2+0.5;">    </div></div><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/polar.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>试着：</p><ul><li>让这些图形动起来。</li><li>结合不同的造型函数来 <strong>雕刻</strong> 图形，制作诸如花，雪花和齿轮。</li><li>用我们在 <strong>造型函数</strong> 章节的 <code>plot()</code> 函数画等高线。</li></ul><h3 id="整合的魅力"><a href="#整合的魅力" class="headerlink" title="整合的魅力"></a>整合的魅力</h3><p>到目前为止，我们知道如何用<a href="../glossary/?search=atan"><code>atan()</code></a>函数来根据角度调整半径以获得不同的图形，以及如何用<code>atan()</code>结合所以和距离场有关的技巧得到可能的效果。</p><p>看下下面来自<a href="https://twitter.com/baldand">Andrew Baldwin</a>的例子。这里的技巧是用极坐标的方式通过定义多边形的边数来构建一个距离场。</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/shapes.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><ul><li><p>用这个例子，改造一个输入位置，指定图形（形状）的顶点数来返回一个距离场（的值）。</p></li><li><p>结合使用 <a href="../glossary/?search=min"><code>min()</code></a> 和 <a href="../glossary/?search=max"><code>max()</code></a> 函数混合距离场。</p></li><li><p>用距离场画个自己感兴趣的logo。</p></li></ul><p>恭喜！你完成了最艰难的部分！休息下让这些概念沉淀一下吧 —— 用Processing 来画简单的形状很容易，但却不到火候。在 shader 的世界里，画形状是很纠结，而且适应这种新的编程范式会有些累人。</p><p>在本章的最后，您将找到[PixelSpirit Deck]（<a href="https://patriciogonzalezvivo.github.io/PixelSpiritDeck/）的链接，这组卡片将帮助您学习新的SDF功能，将其组合到您的设计和使用中">https://patriciogonzalezvivo.github.io/PixelSpiritDeck/）的链接，这组卡片将帮助您学习新的SDF功能，将其组合到您的设计和使用中</a> 在您的着色器上。 卡片组具有预先的学习曲线，因此，每天拿一张卡片并进行练习将推动并挑战您几个月的技能。</p><p>既然现在你知道了如何画形状，我十分肯定你脑袋里已经充满了新的点子。在接下来的章节里你会学习到怎么移动，旋转以及缩放图形。这将使你的创作如虎添翼！</p>]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
    <tags>
      
      <tag>着色器之书</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>着色器之书06</title>
    <link href="/blog/posts/dc594d97.html"/>
    <url>/blog/posts/dc594d97.html</url>
    
    <content type="html"><![CDATA[<h2 id="颜色"><a href="#颜色" class="headerlink" title="颜色"></a>颜色</h2><img src="/blog/posts/dc594d97/klee.jpg" class="" title="Pee.jpg)"><p>我们目前为止还未涉及到GLSL的向量类型。在我们深入向量之前，学习更多关于变量和色彩主题是一个了解向量类型的好方法。</p><p>若你熟悉面向对象的编程范式（或者说编程思维模式），你一定注意到我们以一种类C的 <code>struct</code>的方式访问向量数据的内部分量。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">vec3</span> red = <span class="hljs-type">vec3</span>(<span class="hljs-number">1.0</span>,<span class="hljs-number">0.0</span>,<span class="hljs-number">0.0</span>);<br>red.x = <span class="hljs-number">1.0</span>;<br>red.y = <span class="hljs-number">0.0</span>;<br>red.z = <span class="hljs-number">0.0</span>;<br></code></pre></td></tr></table></figure><p>以x,y,z定义颜色是不是有些奇怪？正因如此，我们有其他方法访问这些变量——以不同的名字。<code>.x</code>, <code>.y</code>, <code>.z</code>也可以被写作<code>.r</code>, <code>.g</code>, <code>.b</code> 和 <code>.s</code>, <code>.t</code>, <code>.p</code>。（<code>.s</code>, <code>.t</code>, <code>.p</code>通常被用做后面章节提到的贴图空间坐标）你也可以通过使用索引位置<code>[0]</code>, <code>[1]</code> 和 <code>[2]</code>来访问向量.</p><p>下面的代码展示了所有访问相同数据的方式：</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">vec4</span> vector;<br>vector[<span class="hljs-number">0</span>] = vector.r = vector.x = vector.s;<br>vector[<span class="hljs-number">1</span>] = vector.g = vector.y = vector.t;<br>vector[<span class="hljs-number">2</span>] = vector.b = vector.z = vector.p;<br>vector[<span class="hljs-number">3</span>] = vector.a = vector.w = vector.q;<br></code></pre></td></tr></table></figure><p>这些指向向量内部变量的不同方式仅仅是设计用来帮助你写出干净代码的术语。着色语言所包含的灵活性为你互换地思考颜色和坐标位置。</p><p>GLSL中向量类型的另一大特点是可以用你需要的任意顺序简单地投射和混合（变量）值。这种能力被（形象地）称为：<strong>鸡尾酒</strong>。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">vec3</span> yellow, magenta, green;<br><br><span class="hljs-comment">// Making Yellow</span><br>yellow.rg = <span class="hljs-type">vec2</span>(<span class="hljs-number">1.0</span>);  <span class="hljs-comment">// Assigning 1. to red and green channels</span><br>yellow[<span class="hljs-number">2</span>] = <span class="hljs-number">0.0</span>;        <span class="hljs-comment">// Assigning 0. to blue channel</span><br><br><span class="hljs-comment">// Making Magenta</span><br>magenta = yellow.rbg;   <span class="hljs-comment">// Assign the channels with green and blue swapped</span><br><br><span class="hljs-comment">// Making Green</span><br>green.rgb = yellow.bgb; <span class="hljs-comment">// Assign the blue channel of Yellow (0) to red and blue channels</span><br></code></pre></td></tr></table></figure><h4 id="个人工具箱"><a href="#个人工具箱" class="headerlink" title="个人工具箱"></a>个人工具箱</h4><p>你可能不习惯用数字拾取颜色—这样非常反直觉。幸运的是，（app store上）有许多可以轻松完成这项任务的程序。寻找一个合适自己的并练习将颜色转化为 <code>vec3</code> 或 <code>vec4</code> 格式。例如，这是我在<a href="http://www.eigenlogik.com/spectrum/mac">Spectrum</a>中使用的的模板。</p><figure class="highlight handlebars"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs handlebars"><span class="xml">vec3(</span><span class="hljs-template-variable">&#123;&#123;<span class="hljs-name">rn</span>&#125;&#125;</span><span class="xml">,</span><span class="hljs-template-variable">&#123;&#123;<span class="hljs-name">gn</span>&#125;&#125;</span><span class="xml">,</span><span class="hljs-template-variable">&#123;&#123;<span class="hljs-name">bn</span>&#125;&#125;</span><span class="xml">)</span><br><span class="xml">vec4(</span><span class="hljs-template-variable">&#123;&#123;<span class="hljs-name">rn</span>&#125;&#125;</span><span class="xml">,</span><span class="hljs-template-variable">&#123;&#123;<span class="hljs-name">gn</span>&#125;&#125;</span><span class="xml">,</span><span class="hljs-template-variable">&#123;&#123;<span class="hljs-name">bn</span>&#125;&#125;</span><span class="xml">,1.0)</span><br></code></pre></td></tr></table></figure><h3 id="混合颜色"><a href="#混合颜色" class="headerlink" title="混合颜色"></a>混合颜色</h3><p>现在你了解到如何定义颜色，是时候将先前所学的整合一下了！在GLSL中，有个十分有用的函数：<a href="../glossary/?search=mix"><code>mix()</code></a>，这个函数让你以百分比混合两个值。猜下百分比的取值范围？没错，0到1！完美！学了这么久的基本功，是时候来用一用了！</p><img src="/blog/posts/dc594d97/mix-f.jpg" class=""><p>看下下列代码中的第18行，这里展示了我们如果是用随时间变化的sin绝对值来混合 <code>colorA</code> 和 <code>colorB</code>。</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/mix.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>试着来 show 一下你所学到的：</p><ul><li>给颜色赋予一个有趣的过渡。想想某种特定的感情。哪种颜色更具代表性？他如何产生？又如何褪去？再想想另外的一种感情以及对应的颜色。然后改变上诉代码中的代表这种情感的开始颜色和结束颜色。Robert Penner 开发了一些列流行的计算机动画塑形函数，被称为<a href="http://easings.net/">缓动函数</a>。你可以研究这些<a href="../edit.php#06/easing.frag">例子</a>并得到启发，但最好你还是自己写一个自己的缓动函数。</li></ul><h3 id="玩玩渐变"><a href="#玩玩渐变" class="headerlink" title="玩玩渐变"></a>玩玩渐变</h3><p><a href="../glossary/?search=mix"><code>mix()</code></a> 函数有更多的用处。我们可以输入两个互相匹配的变量类型而不仅仅是单独的 <code>float</code> 变量，在我们这个例子中用的是 <code>vec3</code>。这样我们便获得了混合颜色单独通道 <code>.r</code>，<code>.g</code> 和 <code>.b</code>的能力。</p><img src="/blog/posts/dc594d97/mix-vec.jpg" class=""><p>试试下面的例子。正如前面一个例子，我们用一条线来可视化根据单位化x坐标的过渡。现在所有通道都按照同样的线性变换过渡。</p><p>现在试试取消25行的注释，看看会发生什么。然后再试试取消26行和27行。记住直线代表了<code>colorA</code> 和 <code>colorB</code>每个通道的混合比例。</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/gradient.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>你可能认出了我们用在25行到27行的造型函数。试着改写他们！是时候把前几张的内容结合起来探索一些新的渐变。试试下列挑战：</p><img src="/blog/posts/dc594d97/turner.jpg" class="" title="William Turner"><ul><li><p>创作一个渐变来代表 William Turner的落日。</p></li><li><p>用 <code>u_time</code> 做个一日出和日落的动画。</p></li><li><p>能用我们所学的做一道彩虹吗？</p></li><li><p>用 <code>step（）</code> 函数在做一个五彩的旗子。</p></li></ul><h3 id="HSB"><a href="#HSB" class="headerlink" title="HSB"></a>HSB</h3><p>我们不能脱离色彩空间来谈论颜色。正如你所知，除了rgb值，有其他不同的方法去描述定义颜色。</p><p><a href="http://en.wikipedia.org/wiki/HSL_and_HSV">HSB</a> 代表色相，饱和度和亮度（或称为值）。这更符合直觉也更有利于组织颜色。稍微花些时间阅读下面的 <code>rgb2hsv()</code> 和 <code>hsv2rgb()</code> 函数。</p><p>将x坐标（位置）映射到Hue值并将y坐标映射到明度，我们就得到了五彩的可见光光谱。这样的色彩空间分布实现起来非常方便，比起RGB，用HSB来拾取颜色更直观。</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/hsb.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><h3 id="极坐标下的HSB"><a href="#极坐标下的HSB" class="headerlink" title="极坐标下的HSB"></a>极坐标下的HSB</h3><p>HSB原本是在极坐标下产生的（以半径和角度定义）而并非在笛卡尔坐标系（基于xy定义）下。将HSB映射到极坐标我们需要取得角度和到像素屏中点的距离。由此我们运用 <a href="../glossary/?search=length"><code>length()</code></a> 函数和 <a href="../glossary/?search=atan"><code>atan(y,x)</code></a> 函数（在GLSL中通常用atan（y,x））。</p><p>当用到矢量和三角学函数时，<code>vec2</code>, <code>vec3</code> 和 <code>vec4</code>被当做向量对待，即使有时候他们代表颜色。我们开始把颜色和向量同等的对待，事实上你会慢慢发现这种理念的灵活性有着相当强大的用途。</p><p><strong>注意</strong>：如果你想了解，除length（）以外的诸多几何函数，例如：<a href="../glossary/?search=distance"><code>distance()</code></a>, <a href="../glossary/?search=dot"><code>dot()</code></a>, <a href="../glossary/?search=cross"><code>cross</code></a>, <a href="../glossary/?search=normalize"><code>normalize()</code></a>, <a href="../glossary/?search=faceforward"><code>faceforward()</code></a>, <a href="../glossary/?search=reflect"><code>reflect()</code></a> 和 <a href="../glossary/?search=refract"><code>refract()</code></a>。 GLSL也有与向量相关的函数：<a href="../glossary/?search=lessThan"><code>lessThan()</code></a>, <a href="../glossary/?search=lessThanEqual"><code>lessThanEqual()</code></a>, <a href="../glossary/?search=greaterThan"><code>greaterThan()</code></a>, <a href="../glossary/?search=greaterThanEqual"><code>greaterThanEqual()</code></a>, <a href="../glossary/?search=equal"><code>equal()</code></a> and <a href="../glossary/?search=notEqual"><code>notEqual()</code></a>。</p><p>一旦我们得到角度和长度，我们需要单位化这些值：0.0到1.0。在27行，<a href="../glossary/?search=atan"><code>atan(y,x)</code></a> 会返回一个介于-PI到PI的弧度值（-3.14 to 3.14），所以我们要将这个返回值除以 <code>TWO_PI</code>（在code顶部定义了）来得到一个-0.5到0.5的值。这样一来，用简单的加法就可以把这个返回值最终映射到0.0到1.0。半径会返回一个最大值0.5（因为我们计算的是到视口中心的距离，而视口中心的范围已经被映射到0.0到1.0），所以我们需要把这个值乘以二来得到一个0到1.0的映射。</p><p>正如你所见，这里我们的游戏都是关于变换和映射到一个0到1这样我们乐于处理的值。</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/hsb-colorwheel.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>来挑战下下面的练习吧：</p><ul><li><p>把极坐标映射的例子改成选择色轮，就像“正忙”的鼠标图标。</p></li><li><p>把造型函数整合进来，来让HSB和RGB的转换中强调某些特定值并且弱化其他的。</p></li></ul><img src="/blog/posts/dc594d97/spectrums.jpg" class="" title="William Home Lizars"><ul><li>如果你仔细观察用来拾色的色轮（见下图），你会发现它用一种根据RYB色彩空间的色谱。例如，红色的对面应该是绿色，但在我们的例子里是青色。你能找到一种修复的方式来让它看起来和下图一样么？[提示：这是用塑形函数的好机会！]</li></ul><img src="/blog/posts/dc594d97/colorwheel.png" class=""><h4 id="注意函数和变量"><a href="#注意函数和变量" class="headerlink" title="注意函数和变量"></a>注意函数和变量</h4><p>在进入下一章之前让我们停下脚步回顾下。复习下之前例子的函数。你会注意到变量类型之前有个限定符 <code>in</code>，在这个 <a href="http://www.shaderific.com/glsl-qualifiers/#inputqualifier"><em>qualifier</em></a> (限定符)例子中它特指这个变量是只读的。在之后的例子中我们会看到可以定义一个 <code>out</code> 或者 <code>inout</code>变量。最后这个 <code>inout</code>，再概念上类似于参照输入一个变量，这意味着我们有可能修改一个传入的变量。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">int</span> newFunction(<span class="hljs-keyword">in</span> <span class="hljs-type">vec4</span> aVec4,   <span class="hljs-comment">// read-only</span><br>                <span class="hljs-keyword">out</span> <span class="hljs-type">vec3</span> aVec3,    <span class="hljs-comment">// write-only</span><br>                <span class="hljs-keyword">inout</span> <span class="hljs-type">int</span> aInt);   <span class="hljs-comment">// read-write</span><br></code></pre></td></tr></table></figure><p>或许你还不相信我们可以用所有这些元素来画一些炫酷的东西。下一章我们会学习如何结合所有这些技巧通过融合 (<em>blending</em>) 空间来创造几何形状。没错。。。融合(<em>blending</em>) 空间。</p>]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
    <tags>
      
      <tag>着色器之书</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>着色器之书05</title>
    <link href="/blog/posts/45501c2d.html"/>
    <url>/blog/posts/45501c2d.html</url>
    
    <content type="html"><![CDATA[<h1 id="算法绘画"><a href="#算法绘画" class="headerlink" title="算法绘画"></a>算法绘画</h1><h2 id="造型函数"><a href="#造型函数" class="headerlink" title="造型函数"></a>造型函数</h2><p>这一章应该叫做宫城先生的粉刷课（来自电影龙威小子的经典桥段）。之前我们把规范化后的 x,y 坐标映射（map）到了红色和绿色通道。本质上说我们是建造了这样一个函数：输入一个二维向量（x，y)，然后返回一个四维向量（r，g，b，a）。但在我们跨维度转换数据之前，我们先从更加…更加简单的开始。我们来建一个只有一维变量的函数。你花越多的时间和精力在这上面，你的 shader 功夫就越厉害。</p><img src="/blog/posts/45501c2d/mr_miyagi.jpg" class="" title="The Karate Kid"><p>接下来的代码结构就是我们的基本功。在它之中我们对规范化的 x 坐标（<code>st.x</code>）进行可视化。有两种途径：一种是用亮度（度量从黑色到白色的渐变过程），另一种是在顶层绘制一条绿色的线（在这种情况下 x 被直接赋值给 y）。不用过分在意绘制函数，我们马上会更加详细地解释它。</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/linear.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p><strong>简注</strong> ：<code>vec3</code> 类型构造器“明白”你想要把一个值赋值到颜色的三个通道里，就像 <code>vec4</code> 明白你想要构建一个四维向量，三维向量加上第四个值（比如颜色的三个值加上透明度）。请参照上面示例的第 19 到 25 行。</p><p>这些代码就是你的基本功；遵守和理解它非常重要。你将会一遍又一遍地回到 0.0 到 1.0 这个区间。你将会掌握融合与构建这些代码的艺术。</p><p>这些 x 与 y（或亮度）之间一对一的关系称作<strong>线性插值</strong>（linear interpolation）。（译者注：插值是离散函数逼近的重要方法，利用它可通过函数在有限个点处的取值状况，估算出函数在其他点处的近似值。因为对计算机来说，屏幕像素是离散的而不是连续的，计算机图形学常用插值来填充图像像素之间的空隙。）现在起我们可以用一些数学函数来改造这些代码行。比如说我们可以做一个求 x 的 5 次幂的曲线。</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/expo.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>很有趣，对吧？试试看把第 19 行的指数改为不同的值，比如：20.0，2.0，1.0，0.0，0.2 或 0.02。理解值和指数之间的关系非常重要。这些数学函数可以让你灵动地控制你的代码，就像是给数据做针灸一样。</p><p><a href="../glossary/?search=pow"><code>pow()</code></a> （求 x 的 y 次幂）是 GLSL 的一个原生函数，GLSL 有很多原生函数。大多数原生函数都是硬件加速的，也就是说如果你正确使用这些函数，你的代码就会跑得更快。</p><p>换掉第 19 行的幂函数，试试看<a href="../glossary/?search=exp"><code>exp()</code></a>（以自然常数 e 为底的指数函数），<a href="../glossary/?search=log"><code>log()</code></a>(对数函数) 和 <a href="../glossary/?search=sqrt"><code>sqrt()</code></a>（平方根函数）。当你用 Pi 来玩的时候有些方程会变得更有趣。在第 5 行我定义了一个宏，使得每当程序调用 <code>PI</code> 的时候就用 <code>3.14159265359</code> 来替换它。</p><h3 id="Step-和-Smoothstep"><a href="#Step-和-Smoothstep" class="headerlink" title="Step 和 Smoothstep"></a>Step 和 Smoothstep</h3><p>GLSL 还有一些独特的原生插值函数可以被硬件加速。</p><p><a href="../glossary/?search=step"><code>step()</code></a> 插值函数需要输入两个参数。第一个是极限或阈值，第二个是我们想要检测或通过的值。对任何小于阈值的值，返回 <code>0.0</code>，大于阈值，则返回 <code>1.0</code>。</p><p>试试看改变下述代码中第 20 行的值。</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/step.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>另一个 GLSL 的特殊函数是 <a href="../glossary/?search=smoothstep"><code>smoothstep()</code></a>。当给定一个范围的上下限和一个数值，这个函数会在已有的范围内给出插值。前两个参数规定转换的开始和结束点，第三个是给出一个值用来插值。</p><div class="container" style="margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/smoothstep.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>在之前的例子中，注意第 12 行，我们用到 smoothstep 在 <code>plot()</code> 函数中画了一条绿色的线。这个函数会对给出的 x 轴上的每个值，在特定的 y 值处制造一个凹凸形变。如何做到呢？通过把两个 <a href="../glossary/?search=smoothstep"><code>smoothstep()</code></a> 连接到一起。来看看下面这个函数，用它替换上面的第 20 行，把它想成是一个垂直切割。背景看起来很像一条线，不是吗？</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">float</span> y = <span class="hljs-built_in">smoothstep</span>(<span class="hljs-number">0.2</span>,<span class="hljs-number">0.5</span>,st.x) - <span class="hljs-built_in">smoothstep</span>(<span class="hljs-number">0.5</span>,<span class="hljs-number">0.8</span>,st.x);<br></code></pre></td></tr></table></figure><h3 id="正弦和余弦函数"><a href="#正弦和余弦函数" class="headerlink" title="正弦和余弦函数"></a>正弦和余弦函数</h3><p>当你想用数学来制造动效，形态或去混合数值，sin 和 cos 就是你的最佳伙伴。</p><p>这两个基础的三角函数是构造圆的极佳工具，就像张小泉的剪刀一样称手。很重要的一点是你需要知道它们是如何运转的，还有如何把它们结合起来。简单来说，当我们给出一个角度（这里采用弧度制），它就会返回半径为一的圆上一个点的 x 坐标（<a href="../glossary/?search=cos">cos</a>）和 y 坐标（<a href="../glossary/?search=sin">sin</a>）。正因为 sin 和 cos 返回的是规范化的值（即值域在 -1 和 1 之间），且如此流畅，这就使得它成为一个极其强大的工具。</p><img src="/blog/posts/45501c2d/sincos-1599050153033.gif" class=""><p>尽管描述三角函数和圆的关系是一件蛮困难的事情，上图动画很棒地做到了这一点，视觉化展现了它们之间的关系。</p><div class="container" style="margin:0;padding:0"> <div class="simpleFunction" data="y = sin(x);"></div></div><p>仔细看 sin 曲线。观察 y 值是如何平滑地在 +1 和 -1 之间变化。就像之前章节关于的 time 的例子中，你可以用 <a href="../glossary/?search=sin"><code>sin()</code></a> 的有节奏的变动给其他东西加动效。如果你是在用浏览器阅读的话你可以改动上述公式，看看曲线会如何变动。(注：不要忘记每行最后要加分号！）</p><p>试试下面的小练习，看看会发生什么：</p><ul><li><p>在 <code>sin</code> 里让 x 加上时间（<code>u_time</code>）。让 sin 曲线随 x 轴<strong>动起来</strong>。</p></li><li><p>在 <code>sin</code> 里用 <code>PI</code> 乘以 x。注意 sin 曲线上下波动的两部分如何<strong>收缩</strong>了，现在 sin 曲线每两个整数循环一次。</p></li><li><p>在 <code>sin</code> 里用时间（ <code>u_time</code>)乘以 x。观察各阶段的循环如何变得越来越<strong>频繁</strong>。注意 u_time 可能已经变得非常大，使得图像难以辨认。</p></li><li><p>给 <a href="../glossary/?search=sin"><code>sin(x)</code></a>（注意不是 sin 里的 x）加 1.0。观察曲线是如何向上<strong>移动</strong>的，现在值域变成了 0.0 到 2.0。</p></li><li><p>给 <a href="../glossary/?search=sin"><code>sin(x)</code></a> 乘以 2.0。观察曲线大小如何<strong>增大</strong>两倍。</p></li><li><p>计算 <code>sin(x)</code> 的绝对值（<a href="../glossary/?search=abs"><code>abs()</code></a>）。现在它看起来就像一个<strong>弹力球</strong>的轨迹。</p></li><li><p>只选取 <code>sin(x)</code> 的小数部分（<a href="../glossary/?search=fract"><code>fract()</code></a>）。</p></li><li><p>使用向正无穷取整（<a href="../glossary/?search=ceil"><code>ceil()</code></a>）和向负无穷取整（<a href="../glossary/?search=floor"><code>floor()</code></a>），使得 sin 曲线变成只有 1 和 -1 的电子波。</p></li></ul><h3 id="其他有用的函数"><a href="#其他有用的函数" class="headerlink" title="其他有用的函数"></a>其他有用的函数</h3><p>最后一个练习中我们介绍了一些新函数。现在我们来一个一个试一遍。依次取消注释下列各行，理解这些函数，观察它们是如何运作的。你一定在奇怪……为什么要这么做呢？Google 一下“generative art”（生成艺术）你就知道了。要知道这些函数就是我们的栅栏。我们现在控制的是它在一维中的移动，上上下下。很快，我们就可以尝试二维、三维甚至四维了！</p><img src="/blog/posts/45501c2d/anthony-mattox-ribbon.jpg" class="" title="Anthony Mattox"><div class="container" style="margin:0;padding:0">    <div class="simpleFunction" data="y = mod(x,0.5); // 返回 x 对 0.5 取模的值    //y = fract(x); // 仅仅返回数的小数部分    //y = ceil(x);  // 向正无穷取整    //y = floor(x); // 向负无穷取整    //y = sign(x);  // 提取 x 的正负号    //y = abs(x);   // 返回 x 的绝对值    //y = clamp(x,0.0,1.0); // 把 x 的值限制在 0.0 到 1.0    //y = min(0.0,x);   // 返回 x 和 0.0 中的较小值    //y = max(0.0,x);   // 返回 x 和 0.0 中的较大值  ">    </div></div><h3 id="造型函数进阶"><a href="#造型函数进阶" class="headerlink" title="造型函数进阶"></a>造型函数进阶</h3><p><a href="http://www.flong.com/">Golan Levin</a> 写过关于更加复杂的造型函数的文档，非常有帮助。把它们引入 GLSL 是非常明智的选择，这将是你的代码的广阔的素材库</p><ul><li><p><a href="http://www.flong.com/texts/code/shapers_poly/">多项式造型函数（Polynomial Shaping Functions）: www.flong.com/texts/code/shapers_poly</a></p></li><li><p><a href="http://www.flong.com/texts/code/shapers_exp/">指数造型函数（Exponential Shaping Functions）: www.flong.com/texts/code/shapers_exp</a></p></li><li><p><a href="http://www.flong.com/texts/code/shapers_circ/">圆与椭圆的造型函数（Circular &amp; Elliptical Shaping Functions）: www.flong.com/texts/code/shapers_circ</a></p></li><li><p><a href="http://www.flong.com/texts/code/shapers_bez/">贝塞尔和其他参数化造型函数（Bezier and Other Parametric Shaping Functions）: www.flong.com/texts/code/shapers_bez</a></p></li></ul><p>就像厨师自主选择辣椒和各种原料，数字艺术家和创意编程者往往钟情于使用他们自己的造型函数。</p><p><a href="http://www.iquilezles.org/">Iñigo Quiles</a> 收集了一套<a href="http://www.iquilezles.org/www/articles/functions/functions.htm">有用的函数</a>。在看过<a href="http://www.iquilezles.org/www/articles/functions/functions.htm">这篇文章</a>后，看看下列函数转换到 GLSL 的样子。注意那些细小的改变，比如给浮点数（float)加小数点“.”，给“C 系函数”换成它们在 GLSL 里的名字，比如不是用 <code>powf()</code> 而是用 <code>pow()</code>：</p><ul><li><a href="../edit.php#05/impulse.frag">Impulse</a></li><li><a href="../edit.php#05/cubicpulse.frag">Cubic Pulse</a></li><li><a href="../edit.php#05/expstep.frag">Exponential Step</a></li><li><a href="../edit.php#05/parabola.frag">Parabola</a></li><li><a href="../edit.php#05/pcurve.frag">Power Curve</a></li></ul><p>给你们看些东西刺激一下斗志，这里有一个非常优雅的例子（作者是 <a href="https://www.shadertoy.com/user/Danguafer">Danguafer</a>，造型函数的空手道黑带）。</p><div class="container" style="margin:0;padding:0"> <iframe style="width:100%;height:auto;margin-bottom:10px" frameborder="0" src="https://www.gishai.top/glsl/#/embed" allowfullscreen></iframe></div><p>在下一章我们会有一些新的进展。我们会先混合各种颜色，然后画些形状。</p><h4 id="练习"><a href="#练习" class="headerlink" title="练习"></a>练习</h4><p>来看看 <a href="http://www.kynd.info/log/">Kynd</a> 帮大家制作的公式表。看他如何结合各种函数及它们的属性，始终控制值的范围在 0.0 到 1.0。好了，现在是你自己练习的时候了！来试试这些函数，记住：熟能生巧。</p><img src="/blog/posts/45501c2d/kynd.png" class="" title="Kynd"><h4 id="填充你的工具箱"><a href="#填充你的工具箱" class="headerlink" title="填充你的工具箱"></a>填充你的工具箱</h4><p>这里有一些工具可以帮你更轻松地可视化这些函数。</p><ul><li>Grapher：如果你是用 MacOS 系统，用 spotlight 搜 <code>grapher</code> 就会看到这个超级方便的工具了。</li></ul><img src="/blog/posts/45501c2d/grapher.png" class="" title="OS X Grapher"><ul><li><a href="http://www.iquilezles.org/apps/graphtoy/">GraphToy</a>：仍然是 <a href="http://www.iquilezles.org">Iñigo Quilez</a> 为大家做的工具，用于在 WebGL 中可视化 GLSL 函数。</li></ul><img src="/blog/posts/45501c2d/graphtoy.png" class="" title="Iñigo Quilez - GraphToy (..&#x2F;..&#x2F;..&#x2F;..&#x2F;Github&#x2F;thebookofshaders&#x2F;05&#x2F;graphtoy.png)"><ul><li><a href="http://tobyschachman.com/Shadershop/">Shadershop</a>：这个超级棒的工具是 <a href="http://tobyschachman.com/">Toby Schachman</a> 的作品。它会以一种极其视觉化和直观的方式教你如何建造复杂的函数。</li></ul><img src="/blog/posts/45501c2d/shadershop.png" class="" title="Toby Schachman">]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
    <tags>
      
      <tag>着色器之书</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>着色器之书04</title>
    <link href="/blog/posts/32572cbb.html"/>
    <url>/blog/posts/32572cbb.html</url>
    
    <content type="html"><![CDATA[<h2 id="运行你的-shader"><a href="#运行你的-shader" class="headerlink" title="运行你的 shader"></a>运行你的 shader</h2><p>现在你可能跃跃欲试，想在你熟悉的平台上小试牛刀了。接下来会有一些比较流行的平台的示例代码，展示如何在这些平台上配置 shader。（在这个 <a href="https://github.com/patriciogonzalezvivo/thebookofshaders/tree/master/04">github 仓库</a> 中有本章的三种平台的示例代码）</p><p><strong>注释 1</strong>：如果你不想用这些平台来运行 shader，且你想在浏览器外使用 shader，你可以下载<a href="https://github.com/patriciogonzalezvivo/glslViewer">glslViewer</a>。这个 MacOS+树莓派程序直接在终端运行，并且是为本书的例子量身打造的。</p><p><strong>注释2</strong>：如果你想用 WebGL 显示 shader，并不关心其他平台，你可以用<a href="https://github.com/patriciogonzalezvivo/glslCanvas">glslCanvas</a> 。这个 web 工具本来是为本书设计的，但是太好用了，所以我常常用在其他项目中。</p><h3 id="Three-js"><a href="#Three-js" class="headerlink" title="Three.js"></a><strong>Three.js</strong></h3><p>为人谦逊而非常有才华的 Ricardo Cabello (也就是 <a href="https://twitter.com/mrdoob">MrDoob</a> )和许多<a href="https://github.com/mrdoob/three.js/graphs/contributors">贡献者</a> 一起搭了可能是 WebGL 最知名的平台，<a href="http://threejs.org/">Three.js</a>。你可以找到无数程序示例，教程，书籍，教你如何用这个 JavaScript 库做出酷炫的 3D 图像。</p><p>下面是一个你需要的例子，教你用 three.js 玩转 shader。注意 <code>id=&quot;fragmentShader&quot;</code>脚本，你要把下面的代码拷到里面。</p><p>下面是一个 HTML 和 JS 的示例，</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;container&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;js/three.min.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;vertexShader&quot;</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;x-shader/x-vertex&quot;</span>&gt;</span><span class="javascript"></span><br><span class="javascript">        <span class="hljs-keyword">void</span> <span class="hljs-function"><span class="hljs-title">main</span>(<span class="hljs-params"></span>)</span> &#123;</span><br><span class="javascript">            gl_Position = vec4( position, <span class="hljs-number">1.0</span> );</span><br><span class="javascript">        &#125;</span><br><span class="javascript">    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;fragmentShader&quot;</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;x-shader/x-fragment&quot;</span>&gt;</span><span class="javascript"></span><br><span class="javascript">        uniform vec2 u_resolution;</span><br><span class="javascript">        uniform float u_time;</span><br><span class="javascript"></span><br><span class="javascript">        <span class="hljs-keyword">void</span> <span class="hljs-function"><span class="hljs-title">main</span>(<span class="hljs-params"></span>)</span> &#123;</span><br><span class="javascript">            vec2 st = gl_FragCoord.xy/u_resolution.xy;</span><br><span class="javascript">            gl_FragColor=vec4(st.x,st.y,<span class="hljs-number">0.0</span>,<span class="hljs-number">1.0</span>);</span><br><span class="javascript">        &#125;</span><br><span class="javascript">    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript"></span><br><span class="javascript">        <span class="hljs-keyword">var</span> container;</span><br><span class="javascript">        <span class="hljs-keyword">var</span> camera, scene, renderer;</span><br><span class="javascript">        <span class="hljs-keyword">var</span> uniforms;</span><br><span class="javascript"></span><br><span class="javascript">        init();</span><br><span class="javascript">        animate();</span><br><span class="javascript"></span><br><span class="javascript">        <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">init</span>(<span class="hljs-params"></span>) </span>&#123;</span><br><span class="javascript">            container = <span class="hljs-built_in">document</span>.getElementById( <span class="hljs-string">&#x27;container&#x27;</span> );</span><br><span class="javascript"></span><br><span class="javascript">            camera = <span class="hljs-keyword">new</span> THREE.Camera();</span><br><span class="javascript">            camera.position.z = <span class="hljs-number">1</span>;</span><br><span class="javascript"></span><br><span class="javascript">            scene = <span class="hljs-keyword">new</span> THREE.Scene();</span><br><span class="javascript"></span><br><span class="javascript">            <span class="hljs-keyword">var</span> geometry = <span class="hljs-keyword">new</span> THREE.PlaneBufferGeometry( <span class="hljs-number">2</span>, <span class="hljs-number">2</span> );</span><br><span class="javascript"></span><br><span class="javascript">            uniforms = &#123;</span><br><span class="javascript">                <span class="hljs-attr">u_time</span>: &#123; <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;f&quot;</span>, <span class="hljs-attr">value</span>: <span class="hljs-number">1.0</span> &#125;,</span><br><span class="javascript">                <span class="hljs-attr">u_resolution</span>: &#123; <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;v2&quot;</span>, <span class="hljs-attr">value</span>: <span class="hljs-keyword">new</span> THREE.Vector2() &#125;</span><br><span class="javascript">            &#125;;</span><br><span class="javascript"></span><br><span class="javascript">            <span class="hljs-keyword">var</span> material = <span class="hljs-keyword">new</span> THREE.ShaderMaterial( &#123;</span><br><span class="javascript">                <span class="hljs-attr">uniforms</span>: uniforms,</span><br><span class="javascript">                <span class="hljs-attr">vertexShader</span>: <span class="hljs-built_in">document</span>.getElementById( <span class="hljs-string">&#x27;vertexShader&#x27;</span> ).textContent,</span><br><span class="javascript">                <span class="hljs-attr">fragmentShader</span>: <span class="hljs-built_in">document</span>.getElementById( <span class="hljs-string">&#x27;fragmentShader&#x27;</span> ).textContent</span><br><span class="javascript">            &#125; );</span><br><span class="javascript"></span><br><span class="javascript">            <span class="hljs-keyword">var</span> mesh = <span class="hljs-keyword">new</span> THREE.Mesh( geometry, material );</span><br><span class="javascript">            scene.add( mesh );</span><br><span class="javascript"></span><br><span class="javascript">            renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer();</span><br><span class="javascript">            renderer.setPixelRatio( <span class="hljs-built_in">window</span>.devicePixelRatio );</span><br><span class="javascript"></span><br><span class="javascript">            container.appendChild( renderer.domElement );</span><br><span class="javascript"></span><br><span class="javascript">            onWindowResize();</span><br><span class="javascript">            <span class="hljs-built_in">window</span>.addEventListener( <span class="hljs-string">&#x27;resize&#x27;</span>, onWindowResize, <span class="hljs-literal">false</span> );</span><br><span class="javascript">        &#125;</span><br><span class="javascript"></span><br><span class="javascript">        <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onWindowResize</span>(<span class="hljs-params"> event </span>) </span>&#123;</span><br><span class="javascript">            renderer.setSize( <span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight );</span><br><span class="javascript">            uniforms.u_resolution.value.x = renderer.domElement.width;</span><br><span class="javascript">            uniforms.u_resolution.value.y = renderer.domElement.height;</span><br><span class="javascript">        &#125;</span><br><span class="javascript"></span><br><span class="javascript">        <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>&#123;</span><br><span class="javascript">            requestAnimationFrame( animate );</span><br><span class="javascript">            render();</span><br><span class="javascript">        &#125;</span><br><span class="javascript"></span><br><span class="javascript">        <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">render</span>(<span class="hljs-params"></span>) </span>&#123;</span><br><span class="javascript">            uniforms.u_time.value += <span class="hljs-number">0.05</span>;</span><br><span class="javascript">            renderer.render( scene, camera );</span><br><span class="javascript">        &#125;</span><br><span class="javascript">    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span><br></code></pre></td></tr></table></figure><h3 id="Processing"><a href="#Processing" class="headerlink" title="Processing"></a><strong>Processing</strong></h3><p>2001年由<a href="http://benfry.com/">Ben Fry</a> 和 <a href="http://reas.com/">Casey Reas</a> 创建，<a href="https://processing.org/">Processing</a>是一个极其简约而强大的环境，非常适合初尝代码的人（至少对于我来是这样）。关于 OpenGL 和视频，<a href="https://codeanticode.wordpress.com/">Andres Colubri</a>为 Processing 平台做了很重要的更新，使得环境非常友好，玩 GLSL shader 比起以前大大容易了。Processing 会在你的 sketch 的 <code>data</code> 文件夹搜索名为 <code>&quot;shader.frag&quot;</code> 的文件。记得把这里的示例代码拷到你的文件夹里然后重命名 shader。</p><figure class="highlight processing"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs processing">PShader <span class="hljs-built_in">shader</span>;<br><br><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span>() &#123;<br>  <span class="hljs-built_in">size</span>(<span class="hljs-number">640</span>, <span class="hljs-number">360</span>, <span class="hljs-literal">P2D</span>);<br>  <span class="hljs-built_in">noStroke</span>();<br><br>  <span class="hljs-built_in">shader</span> = <span class="hljs-built_in">loadShader</span>(<span class="hljs-string">&quot;shader.frag&quot;</span>);<br>&#125;<br><br><span class="hljs-keyword">void</span> <span class="hljs-title">draw</span>() &#123;<br>  <span class="hljs-built_in">shader</span>.<span class="hljs-built_in">set</span>(<span class="hljs-string">&quot;u_resolution&quot;</span>, <span class="hljs-built_in">float</span>(<span class="hljs-built_in">width</span>), <span class="hljs-built_in">float</span>(<span class="hljs-built_in">height</span>));<br>  <span class="hljs-built_in">shader</span>.<span class="hljs-built_in">set</span>(<span class="hljs-string">&quot;u_mouse&quot;</span>, <span class="hljs-built_in">float</span>(<span class="hljs-built_in">mouseX</span>), <span class="hljs-built_in">float</span>(<span class="hljs-built_in">mouseY</span>));<br>  <span class="hljs-built_in">shader</span>.<span class="hljs-built_in">set</span>(<span class="hljs-string">&quot;u_time&quot;</span>, <span class="hljs-built_in">millis</span>() / <span class="hljs-number">1000.0</span>);<br>  <span class="hljs-built_in">shader</span>(<span class="hljs-built_in">shader</span>);<br>  <span class="hljs-built_in">rect</span>(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-built_in">width</span>,<span class="hljs-built_in">height</span>);<br>&#125;<br></code></pre></td></tr></table></figure><p>在 2.1 版之前的版本运行 shader，你需要在你的 shader 文件开头添加以下代码：<br> <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> PROCESSING_COLOR_SHADER</span><br></code></pre></td></tr></table></figure><br> 所以它应该看起来是这样：</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-meta">#ifdef GL_ES</span><br><span class="hljs-keyword">precision</span> <span class="hljs-keyword">mediump</span> <span class="hljs-type">float</span>;<br><span class="hljs-meta">#endif</span><br><br><span class="hljs-meta">#define PROCESSING_COLOR_SHADER</span><br><br><span class="hljs-keyword">uniform</span> <span class="hljs-type">vec2</span> u_resolution;<br><span class="hljs-keyword">uniform</span> <span class="hljs-type">vec3</span> u_mouse;<br><span class="hljs-keyword">uniform</span> <span class="hljs-type">float</span> u_time;<br><br><span class="hljs-type">void</span> main() &#123;<br>    <span class="hljs-type">vec2</span> st = <span class="hljs-built_in">gl_FragCoord</span>.st/u_resolution;<br>    <span class="hljs-built_in">gl_FragColor</span> = <span class="hljs-type">vec4</span>(st.x,st.y,<span class="hljs-number">0.0</span>,<span class="hljs-number">1.0</span>);<br>&#125;<br></code></pre></td></tr></table></figure><p>更多 Processing 的 shader 教程戳 <a href="https://processing.org/tutorials/pshader/">tutorial</a>。</p><h3 id="openFrameworks"><a href="#openFrameworks" class="headerlink" title="openFrameworks"></a><strong>openFrameworks</strong></h3><p>每个人都有自己的舒适区，我的则是<a href="http://openframeworks.cc/">openFrameworks community</a>。这个 C++ 框架打包了 OpenGL 和其他开源 C++ 库。在很多方面它和 Processing 非常像，但是明显和 C++ 编译器打交道一定比较麻烦。和 Processing 很像地，openFrameworks 会在你的 data 文件夹里寻找 shader 文件，所以不要忘记把你的后缀 <code>.frag</code> 的文件拷进去，加载的时候记得改名。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">ofApp::draw</span><span class="hljs-params">()</span></span>&#123;<br>    ofShader shader;<br>    shader.<span class="hljs-built_in">load</span>(<span class="hljs-string">&quot;&quot;</span>,<span class="hljs-string">&quot;shader.frag&quot;</span>);<br><br>    shader.<span class="hljs-built_in">begin</span>();<br>    shader.<span class="hljs-built_in">setUniform1f</span>(<span class="hljs-string">&quot;u_time&quot;</span>, <span class="hljs-built_in">ofGetElapsedTimef</span>());<br>    shader.<span class="hljs-built_in">setUniform2f</span>(<span class="hljs-string">&quot;u_resolution&quot;</span>, <span class="hljs-built_in">ofGetWidth</span>(), <span class="hljs-built_in">ofGetHeight</span>());<br>    <span class="hljs-built_in">ofRect</span>(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-built_in">ofGetWidth</span>(), <span class="hljs-built_in">ofGetHeight</span>());<br>    shader.<span class="hljs-built_in">end</span>();<br>&#125;<br></code></pre></td></tr></table></figure><p>关于 shader 在 openFrameworks 的更多信息请参考这篇<a href="http://openframeworks.cc/ofBook/chapters/shaders.html">excellent tutorial</a>，作者是 <a href="http://thefactoryfactory.com/">Joshua Noble</a>。</p>]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
    <tags>
      
      <tag>着色器之书</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>着色器之书03</title>
    <link href="/blog/posts/ac33b918.html"/>
    <url>/blog/posts/ac33b918.html</url>
    
    <content type="html"><![CDATA[<h2 id="Uniforms"><a href="#Uniforms" class="headerlink" title="Uniforms"></a>Uniforms</h2><p>现在我们知道了 GPU 如何处理并行线程，每个线程负责给完整图像的一部分配置颜色。尽管每个线程和其他线程之间不能有数据交换，但我们能从 CPU 给每个线程输入数据。因为显卡的架构，所有线程的输入值必须<strong>统一</strong>（uniform），而且必须设为<strong>只读</strong>。也就是说，每条线程接收相同的数据，并且是不可改变的数据。</p><p>这些输入值叫做 <code>uniform</code> （统一值），它们的数据类型通常为：<code>float</code>, <code>vec2</code>, <code>vec3</code>, <code>vec4</code>, <code>mat2</code>, <code>mat3</code>, <code>mat4</code>, <code>sampler2D</code> and <code>samplerCube</code>。uniform 值需要数值类型前后一致。且在 shader 的开头，在设定精度之后，就对其进行定义。</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-meta">#ifdef GL_ES</span><br><span class="hljs-keyword">precision</span> <span class="hljs-keyword">mediump</span> <span class="hljs-type">float</span>;<br><span class="hljs-meta">#endif</span><br><br><span class="hljs-keyword">uniform</span> <span class="hljs-type">vec2</span> u_resolution; <span class="hljs-comment">// 画布尺寸（宽，高）</span><br><span class="hljs-keyword">uniform</span> <span class="hljs-type">vec2</span> u_mouse;      <span class="hljs-comment">// 鼠标位置（在屏幕上哪个像素）</span><br><span class="hljs-keyword">uniform</span> <span class="hljs-type">float</span> u_time;  <span class="hljs-comment">// 时间（加载后的秒数）</span><br></code></pre></td></tr></table></figure><p>你可以把 uniforms 想象成连通 GPU 和 CPU 的许多小的桥梁。虽然这些 uniforms 的名字千奇百怪，但是在这一系列的例子中我一直有用到：<code>u_time</code> （时间）, <code>u_resolution</code> （画布尺寸）和 <code>u_mouse</code> （鼠标位置）。按业界传统应在 uniform 值的名字前加 <code>u_</code> ，这样一看即知是 uniform。尽管如此你也还会见到各种各样的名字。比如<a href="https://www.shadertoy.com/">ShaderToy.com</a>就用了如下的名字：</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-keyword">uniform</span> <span class="hljs-type">vec3</span> iResolution;   <span class="hljs-comment">// 视口分辨率（以像素计）</span><br><span class="hljs-keyword">uniform</span> <span class="hljs-type">vec4</span> iMouse;        <span class="hljs-comment">// 鼠标坐标 xy： 当前位置, zw： 点击位置</span><br><span class="hljs-keyword">uniform</span> <span class="hljs-type">float</span> iTime;        <span class="hljs-comment">// shader 运行时间（以秒计）</span><br></code></pre></td></tr></table></figure><p>好了说的足够多了，我们来看看实际操作中的 uniform 吧。在下面的代码中我们使用  <code>u_time</code> 加上一个 sin 函数，来展示图中红色的动态变化。</p><div class="container" style="background:red;margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/time.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>GLSL 还有更多惊喜。GPU 的硬件加速支持我们使用角度，三角函数和指数函数。这里有一些这些函数的介绍：<a href="../glossary/?search=sin"><code>sin()</code></a>, <a href="../glossary/?search=cos"><code>cos()</code></a>, <a href="../glossary/?search=tan"><code>tan()</code></a>, <a href="../glossary/?search=asin"><code>asin()</code></a>, <a href="../glossary/?search=acos"><code>acos()</code></a>, <a href="../glossary/?search=atan"><code>atan()</code></a>, <a href="../glossary/?search=pow"><code>pow()</code></a>, <a href="../glossary/?search=exp"><code>exp()</code></a>, <a href="../glossary/?search=log"><code>log()</code></a>, <a href="../glossary/?search=sqrt"><code>sqrt()</code></a>, <a href="../glossary/?search=abs"><code>abs()</code></a>, <a href="../glossary/?search=sign"><code>sign()</code></a>, <a href="../glossary/?search=floor"><code>floor()</code></a>, <a href="../glossary/?search=ceil"><code>ceil()</code></a>, <a href="../glossary/?search=fract"><code>fract()</code></a>, <a href="../glossary/?search=mod"><code>mod()</code></a>, <a href="../glossary/?search=min"><code>min()</code></a>, <a href="../glossary/?search=max"><code>max()</code></a> 和 <a href="../glossary/?search=clamp"><code>clamp()</code></a>。</p><p>现在又到你来玩的时候了。</p><ul><li><p>降低颜色变化的速率，直到肉眼都看不出来。</p></li><li><p>加速变化，直到颜色静止不动。</p></li><li><p>玩一玩 RGB 三个通道，分别给三个颜色不同的变化速度，看看能不能做出有趣的效果。</p></li></ul><h2 id="gl-FragCoord"><a href="#gl-FragCoord" class="headerlink" title="gl_FragCoord"></a>gl_FragCoord</h2><p>就像 GLSL 有个默认输出值 <code>vec4 gl_FragColor</code> 一样，它也有一个默认输入值（ <code>vec4 gl_FragCoord</code> ）。<code>gl_FragCoord</code>存储了活动线程正在处理的<strong>像素</strong>或<strong>屏幕碎片</strong>的坐标。有了它我们就知道了屏幕上的哪一个线程正在运转。为什么我们不叫 <code>gl_FragCoord</code> uniform （统一值）呢？因为每个像素的坐标都不同，所以我们把它叫做 <strong>varying</strong>（变化值）。</p><div class="container" style="background:red;margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/space.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>上述代码中我们用 <code>gl_FragCoord.xy</code> 除以 <code>u_resolution</code>，对坐标进行了<strong>规范化</strong>。这样做是为了使所有的值落在 <code>0.0</code> 到 <code>1.0</code> 之间，这样就可以轻松把 X 或 Y 的值映射到红色或者绿色通道。</p><p>在 shader 的领域我们没有太多要 debug 的，更多地是试着给变量赋一些很炫的颜色，试图做出一些效果。有时你会觉得用 GLSL 编程就像是把一搜船放到了瓶子里。它同等地困难、美丽而令人满足。</p><img src="/blog/posts/ac33b918/08.png" class=""><p>现在我们来检验一下我们对上面代码的理解程度。</p><ul><li><p>你明白 <code>(0.0,0.0)</code> 坐标在画布上的哪里吗？</p></li><li><p>那 <code>(1.0,0.0)</code>, <code>(0.0,1.0)</code>, <code>(0.5,0.5)</code> 和 <code>(1.0,1.0)</code> 呢？</p></li><li><p>你知道如何用<strong>未</strong>规范化（normalized）的 <code>u_mouse</code> 吗？你可以用它来移动颜色吗？</p></li><li><p>你可以用 <code>u_time</code> 和 <code>u_mouse</code> 来改变颜色的图案吗？不妨琢磨一些有趣的途径。</p></li></ul><p>经过这些小练习后，你可能会好奇还能用强大的 shader 做什么。接下来的章节你会知道如何把你的 shader 和 three.js，Processing，和 openFrameworks 结合起来。</p>]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
    <tags>
      
      <tag>着色器之书</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>着色器之书02</title>
    <link href="/blog/posts/db34898e.html"/>
    <url>/blog/posts/db34898e.html</url>
    
    <content type="html"><![CDATA[<h1 id="开始"><a href="#开始" class="headerlink" title="开始"></a>开始</h1><h2 id="什么是-Fragment-Shader-片段着色器-？"><a href="#什么是-Fragment-Shader-片段着色器-？" class="headerlink" title="什么是 Fragment Shader(片段着色器)？"></a>什么是 Fragment Shader(片段着色器)？</h2><p>在之前的章节我们把 shaders 和古腾堡印刷术相提并论。为什么这样类比呢？更重要的是，什么是 shader？</p><img src="/blog/posts/db34898e/print.png" class="" title="From Letter-by-Letter, Right: William Blades, Left: Rolt-Wheeler (1920)."><p>如果你曾经有用计算机绘图的经验，你就知道在这个过程中你需要画一个圆，然后一个长方形，一条线，一些三角形……直到画出你想要的图像。这个过程很像用手写一封信或一本书 —— 都是一系列的指令，需要你一件一件完成。</p><p>Shaders 也是一系列的指令，但是这些指令会对屏幕上的每个像素同时下达。也就是说，你的代码必须根据像素在屏幕上的不同位置执行不同的操作。就像活字印刷，你的程序就像一个 function（函数），输入位置信息，输出颜色信息，当它编译完之后会以相当快的速度运行。</p><img src="/blog/posts/db34898e/typepress.jpg" class="" title="Chinese movable type"><h2 id="为什么-shaders-运行特别快？"><a href="#为什么-shaders-运行特别快？" class="headerlink" title="为什么 shaders 运行特别快？"></a>为什么 shaders 运行特别快？</h2><p>为了回答这个问题，不得不给大家介绍<strong>并行处理</strong>（parallel processing）的神奇之处。</p><p>想象你的 CPU 是一个大的工业管道，然后每一个任务都是通过这个管道的某些东西 —— 就像一个生产流水线那样。有些任务要比别的大，也就是说要花费更多时间和精力去处理。我们就称它要求更强的处理能力。由于计算机自身的架构，这些任务需要串行；即一次一个地依序完成。现代计算机通常有一组四个处理器，就像这个管道一样运行，一个接一个地处理这些任务，从而使计算机流畅运行。每个管道通常被称为<strong>线程</strong>。</p><img src="/blog/posts/db34898e/00.jpeg" class="" title="CPU"><p>视频游戏和其他图形应用比起别的程序来说，需要高得多的处理能力。因为它们的图形内容需要操作无数像素。想想看，屏幕上的每一个像素都需要计算，而在 3D 游戏中几何和透视也都需要计算。</p><p>让我们回到开始那个关于管道和任务的比喻。屏幕上的每个像素都代表一个最简单的任务。单独来看完成任何一个像素的任务对 CPU 来说都很容易，那么问题来了，屏幕上的每一个像素都需要解决这样的小任务！也就是说，哪怕是对于一个老式的屏幕（分辨率 800x600）来说，都需要每帧处理480000个像素，即每秒进行14400000次计算！是的，这对于微处理器就是大问题了！而对于一个现代的 2800x1800 视网膜屏，每秒运行60帧，就需要每秒进行311040000次计算。图形工程师是如何解决这个问题的？</p><img src="/blog/posts/db34898e/03.jpeg" class=""><p>这个时候，并行处理就是最好的解决方案。比起用三五个强大的微处理器（或者说“管道”）来处理这些信息，用一大堆小的微处理器来并行计算，就要好得多。这就是图形处理器（GPU : Graphic Processor Unit)的来由。</p><img src="/blog/posts/db34898e/04.jpeg" class="" title="GPU"><p>设想一堆小型微处理器排成一个平面的画面，假设每个像素的数据是乒乓球。14400000个乒乓球可以在一秒内阻塞几乎任何管道。但是一面800x600的管道墙，每秒接收30波480000个像素的信息就可以流畅完成。这在更高的分辨率下也是成立的 —— 并行的处理器越多，可以处理的数据流就越大。</p><p>另一个 GPU 的魔法是特殊数学函数可通过硬件加速。非常复杂的数学操作可以直接被微芯片解决，而无须通过软件。这就表示可以有更快的三角和矩阵运算 —— 和电流一样快。</p><h2 id="GLSL是什么？"><a href="#GLSL是什么？" class="headerlink" title="GLSL是什么？"></a>GLSL是什么？</h2><p>GLSL 代表 openGL Shading Language，openGL 着色语言，这是你在接下来章节看到的程序所遵循的具体标准。根据硬件和操作系统的不同，还有其他的着色器（shaders)。这里我们将依照<a href="https://www.khronos.org/opengl/">Khronos Group</a>的规则来执行。了解 OpenGL 的历史将有助于你理解大多数奇怪的约定，所以建议不妨阅读<a href="http://openglbook.com/chapter-0-preface-what-is-opengl.html">openglbook.com/chapter-0-preface-what-is-opengl.html</a>。</p><h2 id="为什么-Shaders-有名地不好学？"><a href="#为什么-Shaders-有名地不好学？" class="headerlink" title="为什么 Shaders 有名地不好学？"></a>为什么 Shaders 有名地不好学？</h2><p>就像蜘蛛侠里的那句名言，能力越大责任越大，并行计算也是如此；GPU 的强大的架构设计也有其限制与不足。</p><p>为了能使许多管线并行运行，每一个线程必须与其他的相独立。我们称这些线程对于其他线程在进行的运算是“盲视”的。这个限制就会使得所有数据必须以相同的方向流动。所以就不可能检查其他线程的输出结果，修改输入的数据，或者把一个线程的输出结果输入给另一个线程。如果允许线程到线程的数据流动将使所有的数据面临威胁。</p><p>并且 GPU 会让所有并行的微处理器（管道们）一直处在忙碌状态；只要它们一有空闲就会接到新的信息。一个线程不可能知道它前一刻在做什么。它可能是在画操作系统界面上的一个按钮，然后渲染了游戏中的一部分天空，然后显示了一封 email 中的一些文字。每个线程不仅是“盲视”的，而且还是“无记忆”的。同时，它要求编写一个通用的规则，依据像素的不同位置依次输出不同的结果。这种抽象性，和盲视、无记忆的限制使得 shaders 在程序员新手中不是很受欢迎。</p><p>但是不要担心！在接下来的章节中，我们会一步一步地，由浅入深地学习着色语言。如果你是在用一个靠谱的浏览器阅读这个教程，你会喜欢边读边玩书中的示例的。好了，不要再浪费时间了，赶快去玩起来吧！ </p><h2 id="Hello-World"><a href="#Hello-World" class="headerlink" title="Hello World"></a>Hello World</h2><p>“Hello world!”通常都是学习一个新语言的第一个例子。这是一个非常简单，只有一行的程序。它既是一个热情的欢迎，也传达了编程所能带来的可能性。</p><p>然而在 GPU 的世界里，第一步就渲染一行文字太难了，所以我们改为选择一个鲜艳的欢迎色，来吧躁起来！</p><div class="container" style="background:red;margin:0;padding:0">    <div class="codeAndCanvas" data="/blog/glsl/hello_world.frag" style="width:100%;height:auto;margin-bottom:10px"></div></div><p>如果你是在线阅读这本书的话，上面的代码都是可以交互的。你可以点击或者改动代码中任何一部分，尽情探索。多亏 GPU 的架构，shader 会<strong>飞速</strong>地编译和更新，这使得你的改动都会立刻出现在你眼前。试试改动第 6 行的值，看会发生什么。</p><p>尽管这几行简单的代码看起来不像有很多内容，我们还是可以据此推测出一些知识点：</p><ol><li><p>shader 语言 有一个 <code>main</code> 函数，会在最后返回颜色值。这点和 C 语言很像。</p></li><li><p>最终的像素颜色取决于预设的全局变量 <code>gl_FragColor</code>。</p></li><li><p>这个类 C 语言有内建的<strong>变量</strong>（像<code>gl_FragColor</code>），<strong>函数</strong>和<strong>数据类型</strong>。在本例中我们刚刚介绍了<code>vec4</code>（四分量浮点向量）。之后我们会见到更多的类型，像 <code>vec3</code> （三分量浮点向量）和 <code>vec2</code> （二分量浮点向量），还有非常著名的：<code>float</code>（单精度浮点型）， <code>int</code>（整型） 和 <code>bool</code>（布尔型）。</p></li><li><p>如果我们仔细观察 <code>vec4</code> 类型，可以推测这四个变元分别响应红，绿，蓝和透明度通道。同时我们也可以看到这些变量是<strong>规范化</strong>的，意思是它们的值是从 0 到 1 的。之后我们会学习如何规范化变量，使得在变量间<strong>map</strong>（映射）数值更加容易。</p></li><li><p>另一个可以从本例看出来的很重要的类 C 语言特征是，预处理程序的宏指令。宏指令是预编译的一部分。有了宏才可以 <code>#define</code> （定义）全局变量和进行一些基础的条件运算（通过使用 <code>#ifdef</code> 和 <code>#endif</code>）。所有的宏都以 <code>#</code> 开头。预编译会在编译前一刻发生，把所有的命令复制到 <code>#defines</code> 里，检查<code>#ifdef</code> 条件句是否已被定义， <code>#ifndef</code> 条件句是否没有被定义。在我们刚刚的“hello world!”的例子中，我们在第 2 行检查了 <code>GL_ES</code> 是否被定义，这个通常用在移动端或浏览器的编译中。</p></li><li><p><code>float</code>类型在 shaders 中非常重要，所以<strong>精度</strong>非常重要。更低的精度会有更快的渲染速度，但是会以质量为代价。你可以选择每一个浮点值的精度。在第一行（<code>precision mediump float;</code>）我们就是设定了所有的浮点值都是中等精度。但我们也可以选择把这个值设为“低”（<code>precision lowp float;</code>）或者“高”（<code>precision highp float;</code>）。</p></li><li><p>最后可能也是最重要的细节是，GLSL 语言规范并不保证变量会被自动转换类别。这句话是什么意思呢？显卡的硬件制造商各有不同的显卡加速方式，但是却被要求有最精简的语言规范。因而，自动强制类型转换并没有包括在其中。在我们的“hello world!”例子中，<code>vec4</code> 精确到单精度浮点，所以应被赋予 <code>float</code> 格式。但是如果你想要代码前后一致，不要之后花费大量时间 debug 的话，最好养成在 <code>float</code> 型数值里加一个 <code>.</code> 的好习惯。如下这种代码就可能不能正常运行：</p></li></ol><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">void</span> main() &#123;<br><span class="hljs-built_in">gl_FragColor</span> = <span class="hljs-type">vec4</span>(<span class="hljs-number">1</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>);<span class="hljs-comment">// 出错</span><br>&#125;<br></code></pre></td></tr></table></figure><p>现在我们已经基本讨论完了“hello world!”例子中所有主要的内容，是时候点击代码，检验一下我们所学的知识了。你会发现出错时程序会编译失败，只留一个寂寞的白屏。你可以试试一些好玩的小点子，比如说：</p><ul><li><p>把单精度浮点值换成整型数值，猜猜你的显卡能不能容忍这个行为。</p></li><li><p>试试把第六行注释掉，不给函数赋任何像素的值。</p></li><li><p>尝试另外写个函数，返回某个颜色，然后在 <code>main()</code> 里面使用这个函数。给个提示，这个函数应该长这样：</p></li></ul><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">vec4</span> red()&#123;<br>    <span class="hljs-keyword">return</span> <span class="hljs-type">vec4</span>(<span class="hljs-number">1.0</span>,<span class="hljs-number">0.0</span>,<span class="hljs-number">0.0</span>,<span class="hljs-number">1.0</span>);<br>&#125;<br></code></pre></td></tr></table></figure><ul><li>有很多种构造 <code>vec4</code> 类型的方式，试试看其他方式。下面就是其中一种方式：</li></ul><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs glsl"><span class="hljs-type">vec4</span> color = <span class="hljs-type">vec4</span>(<span class="hljs-type">vec3</span>(<span class="hljs-number">1.0</span>,<span class="hljs-number">0.0</span>,<span class="hljs-number">1.0</span>),<span class="hljs-number">1.0</span>);<br></code></pre></td></tr></table></figure><p>尽管这个例子看起来不那么刺激，它却是最最基础的 —— 我们把画布上的每一个像素都改成了一个确切的颜色。在接下来的章节中我们将会看到如何用两种输入源来改变像素的颜色：空间（依据像素在屏幕上的位置）和时间（依据页面加载了多少秒）。</p>]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
    <tags>
      
      <tag>着色器之书</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>着色器之书01</title>
    <link href="/blog/posts/423dd834.html"/>
    <url>/blog/posts/423dd834.html</url>
    
    <content type="html"><![CDATA[<h1 id="关于这本书"><a href="#关于这本书" class="headerlink" title="关于这本书"></a>关于这本书</h1><h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><div class="container">    <canvas id="custom" class="canvas" data-fragment-url="/blog/glsl/cmyk-halftone.frag" data-textures="/blog/images/vangogh.jpg" style="width:100%;height:auto" >    </canvas></div><p>上面两幅图是由不同的方式制成的。第一张是梵高一层一层徒手画出来的，需要花费些时间。第二张则是用 4 个像素矩阵分秒钟生成的：一个青色，一个品红，一个黄色，和一个黑色矩阵。关键的区别在于第二张图是用非序列方式实现的（即不是一步一步实现，而是多个同时进行）。</p><p>这本书是关于这个革命性的计算机技术，片段着色器（fragment shaders），它将数字生成的图像提到了新的层次。你可以把它看做当年的古腾堡印刷术。</p><img src="/blog/posts/423dd834/gutenpress.jpg" class="" title="Gutenberg" alt="s press"><p>Fragment shaders（片段着色器）可以让你控制像素在屏幕上的快速渲染。这就是它在各种场合被广泛使用的原因，从手机的视频滤镜到酷炫的的3D视频游戏。</p><img src="/blog/posts/423dd834/journey.jpg" class="" title="Journey by That Game Company"><p>在接下来的章节你会发现这项技术是多么难以置信地快速和强大，还有如何将它应用到专业的和个人的作品中。</p><h2 id="这本书是为谁而写的？"><a href="#这本书是为谁而写的？" class="headerlink" title="这本书是为谁而写的？"></a>这本书是为谁而写的？</h2><p>这本书是写给有代码经验和线性代数、三角学的基本知识的创意编程者、游戏开发者和工程师的，还有那些想要提升他们的作品的图像质量到一个令人激动的新层次的人。（如果你想要学习编程，我强烈推荐你先学习<a href="https://processing.org/">Processing</a>，等你玩起来processing，再回来看这个）。</p><p>这本书会教你如何使用 shaders（着色器）并把它整合进你的项目里，以提升作品的表现力和图形质量。因为GLSL（OpenGL的绘制语言）的shaders 在很多平台都可以编译和运行，你将可以把在这里学的运用到任何使用OpenGL, OpenGL ES 和 WebGL 的环境中。也就是说，你将可以把学到的知识应用到<a href="https://processing.org/">Processing</a>，<a href="http://openframeworks.cc/">openFrameworks</a>，<a href="http://libcinder.org/">Cinder</a>，<a href="http://threejs.org/">Three.js</a>和iOS/Android游戏中。</p><h2 id="这本书包含哪些内容？"><a href="#这本书包含哪些内容？" class="headerlink" title="这本书包含哪些内容？"></a>这本书包含哪些内容？</h2><p>这本书专门关于 GLSL pixel shaders。首先我们会给出shaders的定义；然后我们会学习如何制作程序里的形状，图案，材质，和与之相关的动画。你将会学到基础的着色语言并把它们应用到有用的情景中，比如：图像处理（图像运算，矩阵卷积，模糊，颜色滤镜，查找表及其他效果）和模拟（Conway 的生命游戏，Gray-Scott 反应扩散，水波，水彩效果，Voronoi 细胞等等）。到书的最后我们将看到一系列基于光线跟踪（Ray Marching）的进阶技术。</p><p><strong>每章都会有可以玩的交互的例子。</strong>当你改动代码的时候，你会立刻看到这些变化。一些概念可能会晦涩难懂，而这些可交互的例子会对你学习这些材料非常有益。你越快把这些代码付诸实践，你学习的过程就会越容易。</p><p>这本书里不包括的内容有：</p><ul><li><p>这<strong>不是</strong>一本 openGL 或 webGL 的书。OpenGL/webGL 是一个比GLSL 或 fragment shaders 更大的主题。如果你想要学习 openGL/webGL 推荐看： <a href="https://open.gl/introduction">OpenGL Introduction</a>, <a href="http://www.amazon.com/OpenGL-Programming-Guide-Official-Learning/dp/0321773039/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1424007417&amp;sr=1-1&amp;keywords=open+gl+programming+guide">the 8th edition of the OpenGL Programming Guide</a> (也被叫做红宝书) 或 <a href="http://www.amazon.com/WebGL-Up-Running-Tony-Parisi/dp/144932357X/ref=sr_1_4?s=books&amp;ie=UTF8&amp;qid=1425147254&amp;sr=1-4&amp;keywords=webgl">WebGL: Up and Running</a><br>。</p></li><li><p>这<strong>不是</strong>一本数学书。虽然我们会涉及到很多关于线代和三角学的算法和技术，但我们不会详细解释它。关于数学的问题我推荐手边备一本：<a href="http://www.amazon.com/Mathematics-Programming-Computer-Graphics-Third/dp/1435458869/ref=sr_1_1?ie=UTF8&amp;qid=1424007839&amp;sr=8-1&amp;keywords=mathematics+for+games">3rd Edition of Mathematics for 3D Game Programming and computer Graphics</a> 或 <a href="http://www.amazon.com/Essential-Mathematics-Games-Interactive-Applications/dp/0123742978/ref=sr_1_1?ie=UTF8&amp;qid=1424007889&amp;sr=8-1&amp;keywords=essentials+mathematics+for+developers">2nd Edition of Essential Mathematics for Games and Interactive Applications</a>。</p></li></ul><h2 id="开始学习需要什么准备？"><a href="#开始学习需要什么准备？" class="headerlink" title="开始学习需要什么准备？"></a>开始学习需要什么准备？</h2><p>没什么。如果你有可以运行 WebGL 的浏览器（像Chrome，Firefox或Safari）和网络，点击页面底端的“下一章”按钮就可以开始了。</p><p>此外，基于你有的条件或需求你可以：</p><ul><li><p><a href="https://thebookofshaders.com/appendix/">制作一个离线版的本书</a></p></li><li><p><a href="https://thebookofshaders.com/appendix/">用树莓派而不是浏览器来运行书中示例</a></p></li><li><p><a href="https://thebookofshaders.com/appendix/">做一个PDF版的书用于打印</a></p></li><li><p>用<a href="https://github.com/patriciogonzalezvivo/thebookofshaders">github仓库</a>来帮助解决问题和分享代码</p></li></ul>]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
    <tags>
      
      <tag>着色器之书</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>着色器之书00</title>
    <link href="/blog/posts/353ae8a2.html"/>
    <url>/blog/posts/353ae8a2.html</url>
    
    <content type="html"><![CDATA[<div class="container">    <canvas id="custom" class="canvas" data-fragment-url="/blog/glsl/moon.frag" data-textures="/blog/images/moon.jpg" style="width:15rem;height:15rem;">    </canvas></div><h1 id="The-Book-of-Shaders"><a href="#The-Book-of-Shaders" class="headerlink" title="The Book of Shaders"></a>The Book of Shaders</h1><p><em>by <a href="http://patriciogonzalezvivo.com/">Patricio Gonzalez Vivo</a></em></p><p>这是一本关于 Fragment Shaders（片段着色器）的入门指南，它将一步一步地带你领略其中的纷繁与抽象。</p><h2 id="关于翻译"><a href="#关于翻译" class="headerlink" title="关于翻译"></a>关于翻译</h2><p>这本书是 Patricio 的 the Book of Shaders 的中文翻译。我们希望借此将 Shader 这个有趣有益的工具介绍给更多国人。能力所限，不免有误，如有翻译不当，也请多多指出。</p><p>感谢 Patricio 对我们的翻译的信任和支持。</p><h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="https://thebookofshaders.com/00/?lan=ch">关于这本书</a></li><li>开始<ul><li><a href="https://thebookofshaders.com/01/?lan=ch">什么是片段着色器（Fragment Shader）？</a></li><li><a href="https://thebookofshaders.com/02/?lan=ch">“Hello world!”</a></li><li><a href="https://thebookofshaders.com/03/?lan=ch">Uniforms值</a></li><li><a href="https://thebookofshaders.com/04/?lan=ch">运行你的 shader</a></li></ul></li><li>用算法绘画<ul><li><a href="https://thebookofshaders.com/05/?lan=ch">造型函数</a></li><li><a href="https://thebookofshaders.com/06/?lan=ch">颜色</a></li><li><a href="https://thebookofshaders.com/07/?lan=ch">形状</a></li><li><a href="https://thebookofshaders.com/08/?lan=ch">矩阵</a></li><li><a href="https://thebookofshaders.com/09/?lan=ch">图案</a></li></ul></li><li>生成设计<ul><li><a href="https://thebookofshaders.com/10/?lan=ch">随机</a></li><li><a href="https://thebookofshaders.com/11/?lan=ch">噪声</a></li><li><a href="https://thebookofshaders.com/12/?lan=ch">网格噪声</a></li><li><a href="https://thebookofshaders.com/13/?lan=ch">分形布朗运动</a></li><li>分形</li></ul></li><li>图像处理:<ul><li>纹理</li><li>图像处理</li><li>卷积核</li><li>滤镜</li><li>其他效果</li></ul></li><li>模拟<ul><li>乒乓</li><li>Conway生命游戏</li><li>水波</li><li>水彩</li><li>反应扩散</li></ul></li><li>3D 图形<ul><li>灯光</li><li>法线贴图</li><li>凹凸贴图</li><li>光线跟踪（Ray marching）</li><li>环境贴图 (spherical and cube)</li><li>折射和反射</li></ul></li><li><a href="https://thebookofshaders.com/appendix/">附录:</a> 其他阅读本书的方式<ul><li><a href="https://thebookofshaders.com/appendix/?lan=ch">如何离线阅读此书?</a></li><li><a href="https://thebookofshaders.com/appendix/?lan=ch">如何在树莓派上运行示例程序?</a></li><li><a href="https://thebookofshaders.com/appendix/?lan=ch">如何打印这本书</a></li></ul></li><li><a href="https://thebookofshaders.com/examples/?lan=ch">example gallery</a></li><li><a href="https://thebookofshaders.com/glossary/?lan=ch">词汇表</a></li></ul><h2 id="关于作者"><a href="#关于作者" class="headerlink" title="关于作者"></a>关于作者</h2><p><a href="http://patriciogonzalezvivo.com/">Patricio Gonzalez Vivo</a> (1982, 布宜诺斯艾利斯, 阿根廷) 是一个驻地纽约的艺术家、开发者。他致力于探索有机和人造、模拟信号和数字信号、个体和整体之间的空间。他用代码这种富有表达力的语言来创造更美好的事物。</p><p>Patricio 研习和实践精神疗法（psychotherapy）和表达性艺术治疗（expressive art therapy）。他毕业于 Parsons 的设计与科技专业，且目前执教于此。目前他作为 Mapzen 的图形开发工程师制作一些开源的 mapping tool。</p><p><a href="https://github.com/patriciogonzalezvivo">GitHub</a></p><h2 id="关于译者"><a href="#关于译者" class="headerlink" title="关于译者"></a>关于译者</h2><ul><li><a href="http://tornote.com/">tornote</a> 翻译 00-05 及第 11 章。</li><li><a href="https://github.com/Artrustee">Artrustee</a> 翻译 06-10 章。</li></ul><p>后续章节作者仍在撰写中，如果感兴趣可以在 github 上查看部分后续章节代码。</p><h2 id="致谢"><a href="#致谢" class="headerlink" title="致谢"></a>致谢</h2><p>感谢我的妻子 <a href="http://www.datatelling.com/">Jen Lowe</a>, 感谢她无条件的支持、帮助以及编辑此书。</p><p>感谢 <a href="http://alignedleft.com/">Scott Murray</a> 给予的启发和建议。</p><p>感谢 <a href="https://twitter.com/kyndinfo">Kenichi Yoneda (Kynd)</a> 和 <a href="https://twitter.com/sawakohome">Sawako</a> 的 <a href="https://thebookofshaders.com/?lan=jp">日文版翻译(日本語訳)</a></p><p>感谢 <a href="https://www.facebook.com/tong.lee.9484">Tong Li</a> 和 <a href="https://www.facebook.com/archer.zetta?pnref=story">Yi Zhang</a> 的 <a href="https://thebookofshaders.com/?lan=ch">中文版(Chinese)</a> 翻译。</p><p>感谢 <a href="https://www.facebook.com/fkkcloud">Jae Hyun Yoo</a> 的 <a href="https://thebookofshaders.com/?lan=kr">韩文版 (한국어)</a> 翻译。</p><p>感谢 <a href="http://hinecsoft.com/">Nahuel Coppero (Necsoft)</a> 的 <a href="https://thebookofshaders.com/?lan=es">西班牙语(español)</a> 翻译。</p><p>感谢 <a href="http://karim.naaji.fr/">Karim Naaji</a> 在代码和想法上的支持和贡献。</p><p>感谢所有相信这个项目的人<a href="https://github.com/patriciogonzalezvivo/thebookofshaders/graphs/contributors">contributed with fixes</a> 以及大家的捐赠.</p>]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
    <tags>
      
      <tag>着色器之书</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>重新编译nginx添加新模块的方法</title>
    <link href="/blog/posts/2b8963ab.html"/>
    <url>/blog/posts/2b8963ab.html</url>
    
    <content type="html"><![CDATA[<p>111</p><p>因为使用<code>gzip</code>压缩时,我将<code>gzip_static</code>开启,结果出错了.</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">nginx: [emerg] unknown directive &quot;gzip_static&quot;<br></code></pre></td></tr></table></figure><p>说是没有安装这个模块,但是我已经安装了!!</p><p>下面讲解如何在已经安装过后再次添加新的模块。</p><p>因为源码已经删掉了,重新下载一份</p><h2 id="下载并解压"><a href="#下载并解压" class="headerlink" title="下载并解压"></a>下载并解压</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs shell">wget http://nginx.org/download/nginx-1.18.0.tar.gz<br>tar -xzvf nginx-1.18.0.tar.gz<br>cd nginx-1.18.0<br></code></pre></td></tr></table></figure><h2 id="重新编译"><a href="#重新编译" class="headerlink" title="重新编译"></a>重新编译</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">./configure --prefix=/usr/local/nginx --with-http_ssl_module  --with-http_gzip_static_module<br></code></pre></td></tr></table></figure><h2 id="执行make"><a href="#执行make" class="headerlink" title="执行make"></a>执行make</h2><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sh">make<br></code></pre></td></tr></table></figure><blockquote><p>千万别执行<code>make install</code></p></blockquote><p><code>make</code>完之后在当前目录下的<code>/objs</code>目录下就多了个<code>nginx</code>，这个就是新版本的程序了。</p><h2 id="备份"><a href="#备份" class="headerlink" title="备份"></a>备份</h2><p>以防万一,备份旧的程序</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">cd /usr/local/nginx/sbin/<br>mv nginx nginx_bak<br></code></pre></td></tr></table></figure><h2 id="移植"><a href="#移植" class="headerlink" title="移植"></a>移植</h2><p>将新的<code>nginx</code>程序复制到<code>/usr/local/nginx/sbin/</code>下</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">cp /home/download/softwares/nginx-1.18.0/objs/nginx /usr/local/nginx/sbin/<br></code></pre></td></tr></table></figure><h2 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs shell">/usr/local/nginx/sbin/nginx  -V<br><span class="hljs-meta">#</span><span class="bash"><span class="hljs-comment"># 结果如下</span></span><br>nginx version: nginx/1.18.0<br>built by gcc 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC) <br>built with OpenSSL 1.0.2k-fips  26 Jan 2017<br>TLS SNI support enabled<br>configure arguments: --prefix=/usr/local/nginx --with-http_ssl_module --with-http_gzip_static_module<br></code></pre></td></tr></table></figure><h2 id="平滑启动"><a href="#平滑启动" class="headerlink" title="平滑启动"></a>平滑启动</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs shell"><span class="hljs-meta">#</span><span class="bash"><span class="hljs-comment"># 测试语法的正确性</span></span><br>/usr/local/nginx/sbin/nginx -t<br>nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok<br>nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful<br><span class="hljs-meta">#</span><span class="bash"><span class="hljs-comment"># 启动</span></span><br>/usr/local/nginx/sbin/nginx -s reload<br></code></pre></td></tr></table></figure><h3 id="我新增的模块"><a href="#我新增的模块" class="headerlink" title="我新增的模块"></a>我新增的模块</h3><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs actionscript">--<span class="hljs-keyword">with</span>-http_ssl_module  --<span class="hljs-keyword">with</span>-http_gzip_static_module --<span class="hljs-keyword">with</span>-http_v2_module<br></code></pre></td></tr></table></figure><blockquote><p>参考:<a href="https://www.jb51.net/article/165079.htm">https://www.jb51.net/article/165079.htm</a></p></blockquote>]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
    <tags>
      
      <tag>Nginx</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>使用coding来作为图床的方法</title>
    <link href="/blog/posts/b431f548.html"/>
    <url>/blog/posts/b431f548.html</url>
    
    <content type="html"><![CDATA[<blockquote><p>Hello coding</p></blockquote><p><img src="https://img.gishai.top/source/_posts/使用coding来作为图床的方法/image-20200704110320923.png" alt="image-20200704110320923"><br>本来是想同步hexo与csdn一起写。<br>奈何csdn不支持外链。<br>全部改成<code>&lt;img  src=&quot;&quot; /&gt;</code>的就可以</p>]]></content>
    
    
    <categories>
      
      <category>未分类</category>
      
    </categories>
    
    
  </entry>
  
  
  
  <entry>
    <title>geoserver发布矢量切片说明</title>
    <link href="/blog/posts/9604abcd.html"/>
    <url>/blog/posts/9604abcd.html</url>
    
    <content type="html"><![CDATA[<h2 id="1、安装postgresql-postgis："><a href="#1、安装postgresql-postgis：" class="headerlink" title="1、安装postgresql/postgis："></a>1、安装postgresql/postgis：</h2><img src="/blog/posts/9604abcd/1.1%E6%95%B0%E6%8D%AE%E5%BA%93-postgresql+postgis%E5%AE%89%E8%A3%85.png" class="" title="geoserver发布矢量切片说明"><p>安装注意：先安装postgresql再安装postgis,注意用户名默认是postgres不要改动，密码需要安装者设置，用于后续数据库的登录，过程中一直点下一步即可，注意安装完postgresql弹出下图窗口后直接关闭！</p><img src="/blog/posts/9604abcd/1.2%E6%95%B0%E6%8D%AE%E5%BA%93postgresql%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8F%8Apostgis%E5%AE%89%E8%A3%851.jpg" class="" title="geoserver发布矢量切片说明"><p>安装postgis最后会弹出类似下图窗口，直接都点是</p><img src="/blog/posts/9604abcd/1.3%E6%95%B0%E6%8D%AE%E5%BA%93postgresql%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8F%8Apostgis%E5%AE%89%E8%A3%852.jpg" class="" title="geoserver发布矢量切片说明"><p>最终安装成功！</p><h2 id="2、新建数据库"><a href="#2、新建数据库" class="headerlink" title="2、新建数据库"></a>2、新建数据库</h2><img src="/blog/posts/9604abcd/2.1%E6%95%B0%E6%8D%AE%E5%BA%93-%E6%96%B0%E5%BB%BA%E6%95%B0%E6%8D%AE%E5%BA%93.png" class="" title="geoserver发布矢量切片说明"><p>鼠标单击选中新建的数据库，进去sql语句命名窗口：</p><img src="/blog/posts/9604abcd/2.2%E6%95%B0%E6%8D%AE%E5%BA%93-%E5%A2%9E%E5%8A%A0postgis%E6%89%A9%E5%B1%95.png" class="" title="geoserver发布矢量切片说明"><p>增加postgis扩展，sql语句为 create extension postgis，具体步骤见下图:</p><img src="/blog/posts/9604abcd/2.3%E6%95%B0%E6%8D%AE%E5%BA%93-%E5%A2%9E%E5%8A%A0postgis%E6%89%A9%E5%B1%95.png" class="" title="geoserver发布矢量切片说明"><h2 id="3、使用postgis桌面端工具将shp数据导入postgresql"><a href="#3、使用postgis桌面端工具将shp数据导入postgresql" class="headerlink" title="3、使用postgis桌面端工具将shp数据导入postgresql"></a>3、使用postgis桌面端工具将shp数据导入postgresql</h2><p>打开postgis shapefile import/export manager，准备导入shp数据：</p><img src="/blog/posts/9604abcd/3.1%E6%95%B0%E6%8D%AE%E5%BA%93-%E4%BD%BF%E7%94%A8posgis%E6%A1%8C%E9%9D%A2%E7%AB%AF%E5%B7%A5%E5%85%B7%E5%AF%BC%E5%85%A5shp%E6%95%B0%E6%8D%AE.png" class="" title="geoserver发布矢量切片说明"><p>如果使用上述中国osm数据的话直接全选所有shp文件，点击open后，再点击import即可，需要一些时间等待导入完成，最终导入成功会有successed信息！</p><img src="/blog/posts/9604abcd/3.2%E6%95%B0%E6%8D%AE%E5%BA%93-%E4%BD%BF%E7%94%A8posgis%E6%A1%8C%E9%9D%A2%E7%AB%AF%E5%B7%A5%E5%85%B7%E5%AF%BC%E5%85%A5shp%E6%95%B0%E6%8D%AE.png" class="" title="geoserver发布矢量切片说明"><h2 id="4、安装Geoserver"><a href="#4、安装Geoserver" class="headerlink" title="4、安装Geoserver"></a>4、安装Geoserver</h2><p>网盘中获取安装包，先装jdk，安装完成后在安装的目录bin里面启动geoserver，如图位置：</p><img src="/blog/posts/9604abcd/4.1%E5%90%AF%E5%8A%A8geoserver.png" class="" title="geoserver发布矢量切片说明"><h2 id="5、增加geoserver的矢量切片插件"><a href="#5、增加geoserver的矢量切片插件" class="headerlink" title="5、增加geoserver的矢量切片插件"></a>5、增加geoserver的矢量切片插件</h2><p>下载矢量切片插件geoserver-2_14_2-vectortiles-plugin.rar，解压缩后将jar包拷贝到下图路径下面：</p><img src="/blog/posts/9604abcd/5.1geoserver%E5%A2%9E%E5%8A%A0%E7%9F%A2%E9%87%8F%E5%88%87%E7%89%87%E6%89%A9%E5%B1%95.png" class="" title="geoserver发布矢量切片说明"><h2 id="6、geoserver主界面"><a href="#6、geoserver主界面" class="headerlink" title="6、geoserver主界面"></a>6、geoserver主界面</h2><p>打开geoserver界面 <a href="http://ip:port（默认8080）/geoserver，用户名和密码默认是geoserver/admin">http://ip:port（默认8080）/geoserver，用户名和密码默认是geoserver/admin</a></p><h2 id="7、新建工作区："><a href="#7、新建工作区：" class="headerlink" title="7、新建工作区："></a>7、新建工作区：</h2><p>注意工作区名称命名为chinaosm，后续需要发布的图层均在此新建工作区下完成，因此发布新图层的时候注意选择到正确的工作区！</p><img src="/blog/posts/9604abcd/7.1%E6%96%B0%E5%BB%BA%E5%B7%A5%E4%BD%9C%E5%8C%BA.png" class="" title="geoserver发布矢量切片说明"><h2 id="8、新建数据存储："><a href="#8、新建数据存储：" class="headerlink" title="8、新建数据存储："></a>8、新建数据存储：</h2><img src="/blog/posts/9604abcd/8.1%E6%96%B0%E5%BB%BA%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8.png" class="" title="geoserver发布矢量切片说明"><img src="/blog/posts/9604abcd/8.2%E6%96%B0%E5%BB%BA%E6%95%B0%E6%8D%AE%E6%BA%90.png" class="" title="geoserver发布矢量切片说明"><img src="/blog/posts/9604abcd/8.3%E6%96%B0%E5%BB%BA%E7%9F%A2%E9%87%8F%E6%95%B0%E6%8D%AE%E6%BA%90.png" class="" title="geoserver发布矢量切片说明"><h2 id="9、新建图层："><a href="#9、新建图层：" class="headerlink" title="9、新建图层："></a>9、新建图层：</h2><img src="/blog/posts/9604abcd/9.1%E7%82%B9%E5%87%BB%E5%9B%BE%E5%B1%82%E5%87%86%E5%A4%87%E5%88%9B%E5%BB%BA%E5%9B%BE%E5%B1%82.png" class="" title="geoserver发布矢量切片说明"><p>注意下图列表为====》工作空间名称：数据源名称</p><img src="/blog/posts/9604abcd/9.2%E6%96%B0%E5%BB%BA%E5%9B%BE%E5%B1%82.png" class="" title="geoserver发布矢量切片说明"><img src="/blog/posts/9604abcd/9.3%E6%96%B0%E5%BB%BA%E5%9B%BE%E5%B1%82.png" class="" title="geoserver发布矢量切片说明"><img src="/blog/posts/9604abcd/9.4%E6%96%B0%E5%BB%BA%E5%9B%BE%E5%B1%82.png" class="" title="geoserver发布矢量切片说明"><h2 id="10、新建图层组："><a href="#10、新建图层组：" class="headerlink" title="10、新建图层组："></a>10、新建图层组：</h2><p>新建图层组，作用是将之前新建的工作区中的所有新建图层放置到图层组中，进行统一管理与矢量切片的发布，注意图层组命名为chinaOSM，注意选择需要添加的工作区，添加所有图层！</p><img src="/blog/posts/9604abcd/10.1%E5%9B%BE%E5%B1%82%E7%BB%84%E5%88%9B%E5%BB%BA.png" class="" title="geoserver发布矢量切片说明"><img src="/blog/posts/9604abcd/10.2%E5%9B%BE%E5%B1%82%E7%BB%84%E5%88%9B%E5%BB%BA.png" class="" title="geoserver发布矢量切片说明"><img src="/blog/posts/9604abcd/10.4%E5%9B%BE%E5%B1%82%E7%BB%84%E5%88%9B%E5%BB%BA%E5%BC%80%E5%90%AF%E7%9F%A2%E9%87%8F%E5%88%87%E7%89%87%E6%9C%8D%E5%8A%A1.png" class="" title="geoserver发布矢量切片说明"><p>自此发布矢量切片服务完成！测试可在前端mapbox页面进行测试！</p>]]></content>
    
    
    
  </entry>
  
  
  
  <entry>
    <title>使用 ECharts 插件绘制炫酷图表</title>
    <link href="/blog/posts/9604e2a6.html"/>
    <url>/blog/posts/9604e2a6.html</url>
    
    <content type="html"><![CDATA[<div class="note note-success">            <p>本文由 Fluid 用户授权转载，版权归原作者所有。</p> <p>本文作者：pxxyyz</p> <p>原文地址：<a href="https://pxxyyz.com/posts/15698/">https://pxxyyz.com/posts/15698/</a></p>           </div><h2 id="ECharts使用"><a href="#ECharts使用" class="headerlink" title="ECharts使用"></a>ECharts使用</h2><ul><li>安装<code>hexo-tag-echarts</code>插件</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ npm install hexo-tag-echarts --save<br></code></pre></td></tr></table></figure><ul><li><p>注意：ECharts官网教程-<a href="[https://echarts.apache.org/zh/tutorial.html#5%20%E5%88%86%E9%92%9F%E4%B8%8A%E6%89%8B%20ECharts](https://echarts.apache.org/zh/tutorial.html#5 分钟上手 ECharts">5 分钟上手 ECharts</a>)里的<code>npm install echarts --save</code>并不适合hexo博客，这种安装方式无效，请安装<code>hexo-tag-echarts</code>插件。</p></li><li><p>添加如下js文件</p></li></ul><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs markdown">// 通过jsDelivr的CDN引入echarts<br><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;https://lib.baomitu.com/echarts/4.8.0/echarts.min.js&quot;</span>&gt;</span></span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span><br>// 使用GL里的各种组件时需要添加，否则可不需要<br><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;https://lib.baomitu.com/echarts-gl/1.1.1/echarts-gl.min.js&quot;</span>&gt;</span></span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span><br></code></pre></td></tr></table></figure><ul><li>在markdown文件下添加echarts，格式如下</li></ul><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><br>...<br><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span><br><br>&#123;% echarts 400 &#x27;85%&#x27; %&#125;<br>...<br>&#123;% endecharts %&#125;<br></code></pre></td></tr></table></figure><ul><li><code>&lt;script&gt;</code>中添加定义的变量和函数，若无设定则可删掉<code>&lt;script&gt;&lt;/script&gt;</code></li><li><code>{% echarts 400 '85%' %}</code>和<code>{% endecharts %}</code>之间添加echarts的<code>option</code> 。<ul><li>参数400指定图表展示的高度为400px，85%则指定图表展示的宽度为85%，如不写明这两项参数则默认值为高度400px，宽度81%。</li><li>title：标题组件，包含主标题和副标题。</li><li>legend：图例组件。</li><li>tooltip：提示框组件。</li><li>toolbox：工具栏。内置有导出图片，数据视图，动态类型切换，数据区域缩放，重置五个工具。</li><li>xAxis、yAxis：直角坐标系 grid 中的 x 轴、y轴。</li><li>series：系列列表。每个系列通过<code>type</code>决定自己的图表类型。<ul><li>series-line：折线/面积图</li><li>series-bar：柱状/条形图</li><li>series-pie：饼图</li><li>series-scatter：散点图</li><li>series-radar：雷达图</li><li>series-tree：树图</li><li>series-boxplot：箱形图</li><li>series-candlestick：K线图</li><li>series-heatmap：热力图</li><li>series-graph：关系图</li></ul></li></ul></li><li>多个图表的数据和函数可能会冲突，请注意！</li><li>直接在html中直接绘制，然后用<code>&lt;iframe&gt;&lt;/iframe&gt;</code>展示效果更佳。关于hexo的html文件渲染问题，可以参考<a href="https://pxxyyz.com/posts/13656/">Fluid+自定义html</a>，主要是去掉<code>head</code>部分的说明。</li><li>在html绘图ECharts的格式如下：</li></ul><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;https://lib.baomitu.com/echarts/4.8.0/echarts.min.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br><span class="hljs-comment">&lt;!-- 为 ECharts 准备一个具备大小（宽高）的 DOM --&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;main&quot;</span> <span class="hljs-attr">style</span>=<span class="hljs-string">&quot;width: 600px;height:400px;&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;text/javascript&quot;</span>&gt;</span><span class="javascript"></span><br><span class="javascript">  <span class="hljs-comment">// 基于准备好的dom，初始化echarts实例</span></span><br><span class="javascript">  <span class="hljs-keyword">var</span> myChart = echarts.init(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">&#x27;main&#x27;</span>));</span><br><span class="javascript">  <span class="hljs-comment">// 指定图表的配置项和数据</span></span><br><span class="javascript">  <span class="hljs-keyword">var</span> option = &#123;</span><br><span class="javascript">    ...</span><br><span class="javascript">  &#125;;</span><br><span class="javascript">  <span class="hljs-comment">// 使用刚指定的配置项和数据显示图表。</span></span><br><span class="javascript">  myChart.setOption(option);</span><br><span class="javascript">  <span class="hljs-comment">// 刷新调整</span></span><br><span class="javascript">  <span class="hljs-built_in">window</span>.onresize = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;</span><br><span class="javascript">    myChart.resize();</span><br><span class="javascript">  &#125;</span><br><span class="javascript"></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br></code></pre></td></tr></table></figure><ul><li>部分echart需要引入其他js，如<code>bmap</code>、<code>jquery</code>等，请自行添加。</li><li>使用百度地图的api需要<a href="http://lbsyun.baidu.com/apiconsole/key?application=key">申请密钥(ak)</a>，使用格式如下，注意替换<code>FAKE_AK</code>。</li></ul><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;text/javascript&quot;</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;https://api.map.baidu.com/api?v=2.0&amp;ak=FAKE_AK&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;text/javascript&quot;</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;https://api.map.baidu.com/getscript?v=2.0&amp;ak=FAKE_AK&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br></code></pre></td></tr></table></figure><script src="https://lib.baomitu.com/echarts/4.8.0/echarts.min.js"></script><script src="https://api.map.baidu.com/getscript?v=2.0&ak=84y4lPUPCHIrwRUQPc61uBewdYZ1pHM2"></script><script src="https://lib.baomitu.com/echarts-gl/1.1.1/echarts-gl.min.js"></script><h2 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</h2><div class="note note-success">            <p>下面给出一些<a href="https://echarts.apache.org/examples/zh/index.html">echarts官方实例</a>，大多数都可以<span class="label label-primary"><strong>交互</strong></span>。</p>           </div><h3 id="折线图Line"><a href="#折线图Line" class="headerlink" title="折线图Line"></a>折线图Line</h3><ul><li><a href="https://echarts.apache.org/examples/zh/editor.html?c=area-stack">Stacked area chart</a></li></ul><div id="echarts1319" style="width: 85%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts1319'));        // 指定图表的配置项和数据        var option = option = {    title: {        text: '堆叠区域图'    },    tooltip: {        trigger: 'axis',        axisPointer: {            type: 'cross',            label: {                backgroundColor: '#6a7985'            }        }    },    legend: {        data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']    },    toolbox: {        feature: {            saveAsImage: {}        }    },    grid: {        left: '3%',        right: '4%',        bottom: '3%',        containLabel: true    },    xAxis: [        {            type: 'category',            boundaryGap: false,            data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']        }    ],    yAxis: [        {            type: 'value'        }    ],    series: [        {            name: '邮件营销',            type: 'line',            stack: '总量',            areaStyle: {},            data: [120, 132, 101, 134, 90, 230, 210]        },        {            name: '联盟广告',            type: 'line',            stack: '总量',            areaStyle: {},            data: [220, 182, 191, 234, 290, 330, 310]        },        {            name: '视频广告',            type: 'line',            stack: '总量',            areaStyle: {},            data: [150, 232, 201, 154, 190, 330, 410]        },        {            name: '直接访问',            type: 'line',            stack: '总量',            areaStyle: {},            data: [320, 332, 301, 334, 390, 330, 320]        },        {            name: '搜索引擎',            type: 'line',            stack: '总量',            label: {                normal: {                    show: true,                    position: 'top'                }            },            areaStyle: {},            data: [820, 932, 901, 934, 1290, 1330, 1320]        }    ]};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script><ul><li><a href="https://echarts.apache.org/examples/zh/editor.html?c=area-pieces">Area Pieces</a></li></ul><div id="echarts174" style="width: 85%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts174'));        // 指定图表的配置项和数据        var option = option = {    xAxis: {        type: 'category',        boundaryGap: false    },    yAxis: {        type: 'value',        boundaryGap: [0, '30%']    },    visualMap: {        type: 'piecewise',        show: false,        dimension: 0,        seriesIndex: 0,        pieces: [{            gt: 1,            lt: 3,            color: 'rgba(0, 180, 0, 0.5)'        }, {            gt: 5,            lt: 7,            color: 'rgba(0, 180, 0, 0.5)'        }]    },    series: [        {            type: 'line',            smooth: 0.6,            symbol: 'none',            lineStyle: {                color: 'green',                width: 5            },            markLine: {                symbol: ['none', 'none'],                label: {show: false},                data: [                    {xAxis: 1},                    {xAxis: 3},                    {xAxis: 5},                    {xAxis: 7}                ]            },            areaStyle: {},            data: [                ['2019-10-10', 200],                ['2019-10-11', 400],                ['2019-10-12', 650],                ['2019-10-13', 500],                ['2019-10-14', 250],                ['2019-10-15', 300],                ['2019-10-16', 450],                ['2019-10-17', 300],                ['2019-10-18', 100]            ]        }    ]};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script><ul><li><a href="https://echarts.apache.org/examples/zh/editor.html?c=area-rainfall">Rainfall</a></li></ul><div id="echarts5031" style="width: 85%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts5031'));        // 指定图表的配置项和数据        var option = option = {    title: {        text: '雨量流量关系图',        subtext: '数据来自西安兰特水电测控技术有限公司',        left: 'center',        align: 'right'    },    grid: {        bottom: 80    },    toolbox: {        feature: {            dataZoom: {                yAxisIndex: 'none'            },            restore: {},            saveAsImage: {}        }    },    tooltip: {        trigger: 'axis',        axisPointer: {            type: 'cross',            animation: false,            label: {                backgroundColor: '#505765'            }        }    },    legend: {        data: ['流量', '降雨量'],        left: 10    },    dataZoom: [        {            show: true,            realtime: true,            start: 65,            end: 85        },        {            type: 'inside',            realtime: true,            start: 65,            end: 85        }    ],    xAxis: [        {            type: 'category',            boundaryGap: false,            axisLine: {onZero: false},            data: [                '2009/6/12 2:00', '2009/6/12 3:00', '2009/6/12 4:00', '2009/6/12 5:00', '2009/6/12 6:00', '2009/6/12 7:00', '2009/6/12 8:00', '2009/6/12 9:00', '2009/6/12 10:00', '2009/6/12 11:00', '2009/6/12 12:00', '2009/6/12 13:00', '2009/6/12 14:00', '2009/6/12 15:00', '2009/6/12 16:00', '2009/6/12 17:00', '2009/6/12 18:00', '2009/6/12 19:00', '2009/6/12 20:00', '2009/6/12 21:00', '2009/6/12 22:00', '2009/6/12 23:00',                '2009/6/13 0:00', '2009/6/13 1:00', '2009/6/13 2:00', '2009/6/13 3:00', '2009/6/13 4:00', '2009/6/13 5:00', '2009/6/13 6:00', '2009/6/13 7:00', '2009/6/13 8:00', '2009/6/13 9:00', '2009/6/13 10:00', '2009/6/13 11:00', '2009/6/13 12:00', '2009/6/13 13:00', '2009/6/13 14:00', '2009/6/13 15:00', '2009/6/13 16:00', '2009/6/13 17:00', '2009/6/13 18:00', '2009/6/13 19:00', '2009/6/13 20:00', '2009/6/13 21:00', '2009/6/13 22:00', '2009/6/13 23:00',                '2009/6/14 0:00', '2009/6/14 1:00', '2009/6/14 2:00', '2009/6/14 3:00', '2009/6/14 4:00', '2009/6/14 5:00', '2009/6/14 6:00', '2009/6/14 7:00', '2009/6/14 8:00', '2009/6/14 9:00', '2009/6/14 10:00', '2009/6/14 11:00', '2009/6/14 12:00', '2009/6/14 13:00', '2009/6/14 14:00', '2009/6/14 15:00', '2009/6/14 16:00', '2009/6/14 17:00', '2009/6/14 18:00', '2009/6/14 19:00', '2009/6/14 20:00', '2009/6/14 21:00', '2009/6/14 22:00', '2009/6/14 23:00',                '2009/6/15 0:00', '2009/6/15 1:00', '2009/6/15 2:00', '2009/6/15 3:00', '2009/6/15 4:00', '2009/6/15 5:00', '2009/6/15 6:00', '2009/6/15 7:00', '2009/6/15 8:00', '2009/6/15 9:00', '2009/6/15 10:00', '2009/6/15 11:00', '2009/6/15 12:00', '2009/6/15 13:00', '2009/6/15 14:00', '2009/6/15 15:00', '2009/6/15 16:00', '2009/6/15 17:00', '2009/6/15 18:00', '2009/6/15 19:00', '2009/6/15 20:00', '2009/6/15 21:00', '2009/6/15 22:00', '2009/6/15 23:00',                '2009/6/15 0:00', '2009/6/16 1:00', '2009/6/16 2:00', '2009/6/16 3:00', '2009/6/16 4:00', '2009/6/16 5:00', '2009/6/16 6:00', '2009/6/16 7:00', '2009/6/16 8:00', '2009/6/16 9:00', '2009/6/16 10:00', '2009/6/16 11:00', '2009/6/16 12:00', '2009/6/16 13:00', '2009/6/16 14:00', '2009/6/16 15:00', '2009/6/16 16:00', '2009/6/16 17:00', '2009/6/16 18:00', '2009/6/16 19:00', '2009/6/16 20:00', '2009/6/16 21:00', '2009/6/16 22:00', '2009/6/16 23:00',                '2009/6/15 0:00', '2009/6/17 1:00', '2009/6/17 2:00', '2009/6/17 3:00', '2009/6/17 4:00', '2009/6/17 5:00', '2009/6/17 6:00', '2009/6/17 7:00', '2009/6/17 8:00', '2009/6/17 9:00', '2009/6/17 10:00', '2009/6/17 11:00', '2009/6/17 12:00', '2009/6/17 13:00', '2009/6/17 14:00', '2009/6/17 15:00', '2009/6/17 16:00', '2009/6/17 17:00', '2009/6/17 18:00', '2009/6/17 19:00', '2009/6/17 20:00', '2009/6/17 21:00', '2009/6/17 22:00', '2009/6/17 23:00',                '2009/6/18 0:00', '2009/6/18 1:00', '2009/6/18 2:00', '2009/6/18 3:00', '2009/6/18 4:00', '2009/6/18 5:00', '2009/6/18 6:00', '2009/6/18 7:00', '2009/6/18 8:00', '2009/6/18 9:00', '2009/6/18 10:00', '2009/6/18 11:00', '2009/6/18 12:00', '2009/6/18 13:00', '2009/6/18 14:00', '2009/6/18 15:00', '2009/6/18 16:00', '2009/6/18 17:00', '2009/6/18 18:00', '2009/6/18 19:00', '2009/6/18 20:00', '2009/6/18 21:00', '2009/6/18 22:00', '2009/6/18 23:00',                '2009/6/15 0:00', '2009/6/19 1:00', '2009/6/19 2:00', '2009/6/19 3:00', '2009/6/19 4:00', '2009/6/19 5:00', '2009/6/19 6:00', '2009/6/19 7:00', '2009/6/19 8:00', '2009/6/19 9:00', '2009/6/19 10:00', '2009/6/19 11:00', '2009/6/19 12:00', '2009/6/19 13:00', '2009/6/19 14:00', '2009/6/19 15:00', '2009/6/19 16:00', '2009/6/19 17:00', '2009/6/19 18:00', '2009/6/19 19:00', '2009/6/19 20:00', '2009/6/19 21:00', '2009/6/19 22:00', '2009/6/19 23:00',                '2009/6/20 0:00', '2009/6/20 1:00', '2009/6/20 2:00', '2009/6/20 3:00', '2009/6/20 4:00', '2009/6/20 5:00', '2009/6/20 6:00', '2009/6/20 7:00', '2009/6/20 8:00', '2009/6/20 9:00', '2009/6/20 10:00', '2009/6/20 11:00', '2009/6/20 12:00', '2009/6/20 13:00', '2009/6/20 14:00', '2009/6/20 15:00', '2009/6/20 16:00', '2009/6/20 17:00', '2009/6/20 18:00', '2009/6/20 19:00', '2009/6/20 20:00', '2009/6/20 21:00', '2009/6/20 22:00', '2009/6/20 23:00',                '2009/6/21 0:00', '2009/6/21 1:00', '2009/6/21 2:00', '2009/6/21 3:00', '2009/6/21 4:00', '2009/6/21 5:00', '2009/6/21 6:00', '2009/6/21 7:00', '2009/6/21 8:00', '2009/6/21 9:00', '2009/6/21 10:00', '2009/6/21 11:00', '2009/6/21 12:00', '2009/6/21 13:00', '2009/6/21 14:00', '2009/6/21 15:00', '2009/6/21 16:00', '2009/6/21 17:00', '2009/6/21 18:00', '2009/6/21 19:00', '2009/6/21 20:00', '2009/6/21 21:00', '2009/6/21 22:00', '2009/6/21 23:00',                '2009/6/22 0:00', '2009/6/22 1:00', '2009/6/22 2:00', '2009/6/22 3:00', '2009/6/22 4:00', '2009/6/22 5:00', '2009/6/22 6:00', '2009/6/22 7:00', '2009/6/22 8:00', '2009/6/22 9:00', '2009/6/22 10:00', '2009/6/22 11:00', '2009/6/22 12:00', '2009/6/22 13:00', '2009/6/22 14:00', '2009/6/22 15:00', '2009/6/22 16:00', '2009/6/22 17:00', '2009/6/22 18:00', '2009/6/22 19:00', '2009/6/22 20:00', '2009/6/22 21:00', '2009/6/22 22:00', '2009/6/22 23:00',                '2009/6/23 0:00', '2009/6/23 1:00', '2009/6/23 2:00', '2009/6/23 3:00', '2009/6/23 4:00', '2009/6/23 5:00', '2009/6/23 6:00', '2009/6/23 7:00', '2009/6/23 8:00', '2009/6/23 9:00', '2009/6/23 10:00', '2009/6/23 11:00', '2009/6/23 12:00', '2009/6/23 13:00', '2009/6/23 14:00', '2009/6/23 15:00', '2009/6/23 16:00', '2009/6/23 17:00', '2009/6/23 18:00', '2009/6/23 19:00', '2009/6/23 20:00', '2009/6/23 21:00', '2009/6/23 22:00', '2009/6/23 23:00',                '2009/6/24 0:00', '2009/6/24 1:00', '2009/6/24 2:00', '2009/6/24 3:00', '2009/6/24 4:00', '2009/6/24 5:00', '2009/6/24 6:00', '2009/6/24 7:00', '2009/6/24 8:00', '2009/6/24 9:00', '2009/6/24 10:00', '2009/6/24 11:00', '2009/6/24 12:00', '2009/6/24 13:00', '2009/6/24 14:00', '2009/6/24 15:00', '2009/6/24 16:00', '2009/6/24 17:00', '2009/6/24 18:00', '2009/6/24 19:00', '2009/6/24 20:00', '2009/6/24 21:00', '2009/6/24 22:00', '2009/6/24 23:00',                '2009/6/25 0:00', '2009/6/25 1:00', '2009/6/25 2:00', '2009/6/25 3:00', '2009/6/25 4:00', '2009/6/25 5:00', '2009/6/25 6:00', '2009/6/25 7:00', '2009/6/25 8:00', '2009/6/25 9:00', '2009/6/25 10:00', '2009/6/25 11:00', '2009/6/25 12:00', '2009/6/25 13:00', '2009/6/25 14:00', '2009/6/25 15:00', '2009/6/25 16:00', '2009/6/25 17:00', '2009/6/25 18:00', '2009/6/25 19:00', '2009/6/25 20:00', '2009/6/25 21:00', '2009/6/25 22:00', '2009/6/25 23:00',                '2009/6/26 0:00', '2009/6/26 1:00', '2009/6/26 2:00', '2009/6/26 3:00', '2009/6/26 4:00', '2009/6/26 5:00', '2009/6/26 6:00', '2009/6/26 7:00', '2009/6/26 8:00', '2009/6/26 9:00', '2009/6/26 10:00', '2009/6/26 11:00', '2009/6/26 12:00', '2009/6/26 13:00', '2009/6/26 14:00', '2009/6/26 15:00', '2009/6/26 16:00', '2009/6/26 17:00', '2009/6/26 18:00', '2009/6/26 19:00', '2009/6/26 20:00', '2009/6/26 21:00', '2009/6/26 22:00', '2009/6/26 23:00',                '2009/6/27 0:00', '2009/6/27 1:00', '2009/6/27 2:00', '2009/6/27 3:00', '2009/6/27 4:00', '2009/6/27 5:00', '2009/6/27 6:00', '2009/6/27 7:00', '2009/6/27 8:00', '2009/6/27 9:00', '2009/6/27 10:00', '2009/6/27 11:00', '2009/6/27 12:00', '2009/6/27 13:00', '2009/6/27 14:00', '2009/6/27 15:00', '2009/6/27 16:00', '2009/6/27 17:00', '2009/6/27 18:00', '2009/6/27 19:00', '2009/6/27 20:00', '2009/6/27 21:00', '2009/6/27 22:00', '2009/6/27 23:00',                '2009/6/28 0:00', '2009/6/28 1:00', '2009/6/28 2:00', '2009/6/28 3:00', '2009/6/28 4:00', '2009/6/28 5:00', '2009/6/28 6:00', '2009/6/28 7:00', '2009/6/28 8:00', '2009/6/28 9:00', '2009/6/28 10:00', '2009/6/28 11:00', '2009/6/28 12:00', '2009/6/28 13:00', '2009/6/28 14:00', '2009/6/28 15:00', '2009/6/28 16:00', '2009/6/28 17:00', '2009/6/28 18:00', '2009/6/28 19:00', '2009/6/28 20:00', '2009/6/28 21:00', '2009/6/28 22:00', '2009/6/28 23:00',                '2009/6/29 0:00', '2009/6/29 1:00', '2009/6/29 2:00', '2009/6/29 3:00', '2009/6/29 4:00', '2009/6/29 5:00', '2009/6/29 6:00', '2009/6/29 7:00', '2009/6/29 8:00', '2009/6/29 9:00', '2009/6/29 10:00', '2009/6/29 11:00', '2009/6/29 12:00', '2009/6/29 13:00', '2009/6/29 14:00', '2009/6/29 15:00', '2009/6/29 16:00', '2009/6/29 17:00', '2009/6/29 18:00', '2009/6/29 19:00', '2009/6/29 20:00', '2009/6/29 21:00', '2009/6/29 22:00', '2009/6/29 23:00',                '2009/6/30 0:00', '2009/6/30 1:00', '2009/6/30 2:00', '2009/6/30 3:00', '2009/6/30 4:00', '2009/6/30 5:00', '2009/6/30 6:00', '2009/6/30 7:00', '2009/6/30 8:00', '2009/6/30 9:00', '2009/6/30 10:00', '2009/6/30 11:00', '2009/6/30 12:00', '2009/6/30 13:00', '2009/6/30 14:00', '2009/6/30 15:00', '2009/6/30 16:00', '2009/6/30 17:00', '2009/6/30 18:00', '2009/6/30 19:00', '2009/6/30 20:00', '2009/6/30 21:00', '2009/6/30 22:00', '2009/6/30 23:00',                '2009/7/1 0:00', '2009/7/1 1:00', '2009/7/1 2:00', '2009/7/1 3:00', '2009/7/1 4:00', '2009/7/1 5:00', '2009/7/1 6:00', '2009/7/1 7:00', '2009/7/1 8:00', '2009/7/1 9:00', '2009/7/1 10:00', '2009/7/1 11:00', '2009/7/1 12:00', '2009/7/1 13:00', '2009/7/1 14:00', '2009/7/1 15:00', '2009/7/1 16:00', '2009/7/1 17:00', '2009/7/1 18:00', '2009/7/1 19:00', '2009/7/1 20:00', '2009/7/1 21:00', '2009/7/1 22:00', '2009/7/1 23:00',                '2009/7/2 0:00', '2009/7/2 1:00', '2009/7/2 2:00', '2009/7/2 3:00', '2009/7/2 4:00', '2009/7/2 5:00', '2009/7/2 6:00', '2009/7/2 7:00', '2009/7/2 8:00', '2009/7/2 9:00', '2009/7/2 10:00', '2009/7/2 11:00', '2009/7/2 12:00', '2009/7/2 13:00', '2009/7/2 14:00', '2009/7/2 15:00', '2009/7/2 16:00', '2009/7/2 17:00', '2009/7/2 18:00', '2009/7/2 19:00', '2009/7/2 20:00', '2009/7/2 21:00', '2009/7/2 22:00', '2009/7/2 23:00',                '2009/7/3 0:00', '2009/7/3 1:00', '2009/7/3 2:00', '2009/7/3 3:00', '2009/7/3 4:00', '2009/7/3 5:00', '2009/7/3 6:00', '2009/7/3 7:00', '2009/7/3 8:00', '2009/7/3 9:00', '2009/7/3 10:00', '2009/7/3 11:00', '2009/7/3 12:00', '2009/7/3 13:00', '2009/7/3 14:00', '2009/7/3 15:00', '2009/7/3 16:00', '2009/7/3 17:00', '2009/7/3 18:00', '2009/7/3 19:00', '2009/7/3 20:00', '2009/7/3 21:00', '2009/7/3 22:00', '2009/7/3 23:00',                '2009/7/4 0:00', '2009/7/4 1:00', '2009/7/4 2:00', '2009/7/4 3:00', '2009/7/4 4:00', '2009/7/4 5:00', '2009/7/4 6:00', '2009/7/4 7:00', '2009/7/4 8:00', '2009/7/4 9:00', '2009/7/4 10:00', '2009/7/4 11:00', '2009/7/4 12:00', '2009/7/4 13:00', '2009/7/4 14:00', '2009/7/4 15:00', '2009/7/4 16:00', '2009/7/4 17:00', '2009/7/4 18:00', '2009/7/4 19:00', '2009/7/4 20:00', '2009/7/4 21:00', '2009/7/4 22:00', '2009/7/4 23:00',                '2009/7/5 0:00', '2009/7/5 1:00', '2009/7/5 2:00', '2009/7/5 3:00', '2009/7/5 4:00', '2009/7/5 5:00', '2009/7/5 6:00', '2009/7/5 7:00', '2009/7/5 8:00', '2009/7/5 9:00', '2009/7/5 10:00', '2009/7/5 11:00', '2009/7/5 12:00', '2009/7/5 13:00', '2009/7/5 14:00', '2009/7/5 15:00', '2009/7/5 16:00', '2009/7/5 17:00', '2009/7/5 18:00', '2009/7/5 19:00', '2009/7/5 20:00', '2009/7/5 21:00', '2009/7/5 22:00', '2009/7/5 23:00',                '2009/7/6 0:00', '2009/7/6 1:00', '2009/7/6 2:00', '2009/7/6 3:00', '2009/7/6 4:00', '2009/7/6 5:00', '2009/7/6 6:00', '2009/7/6 7:00', '2009/7/6 8:00', '2009/7/6 9:00', '2009/7/6 10:00', '2009/7/6 11:00', '2009/7/6 12:00', '2009/7/6 13:00', '2009/7/6 14:00', '2009/7/6 15:00', '2009/7/6 16:00', '2009/7/6 17:00', '2009/7/6 18:00', '2009/7/6 19:00', '2009/7/6 20:00', '2009/7/6 21:00', '2009/7/6 22:00', '2009/7/6 23:00',                '2009/7/7 0:00', '2009/7/7 1:00', '2009/7/7 2:00', '2009/7/7 3:00', '2009/7/7 4:00', '2009/7/7 5:00', '2009/7/7 6:00', '2009/7/7 7:00', '2009/7/7 8:00', '2009/7/7 9:00', '2009/7/7 10:00', '2009/7/7 11:00', '2009/7/7 12:00', '2009/7/7 13:00', '2009/7/7 14:00', '2009/7/7 15:00', '2009/7/7 16:00', '2009/7/7 17:00', '2009/7/7 18:00', '2009/7/7 19:00', '2009/7/7 20:00', '2009/7/7 21:00', '2009/7/7 22:00', '2009/7/7 23:00',                '2009/7/8 0:00', '2009/7/8 1:00', '2009/7/8 2:00', '2009/7/8 3:00', '2009/7/8 4:00', '2009/7/8 5:00', '2009/7/8 6:00', '2009/7/8 7:00', '2009/7/8 8:00', '2009/7/8 9:00', '2009/7/8 10:00', '2009/7/8 11:00', '2009/7/8 12:00', '2009/7/8 13:00', '2009/7/8 14:00', '2009/7/8 15:00', '2009/7/8 16:00', '2009/7/8 17:00', '2009/7/8 18:00', '2009/7/8 19:00', '2009/7/8 20:00', '2009/7/8 21:00', '2009/7/8 22:00', '2009/7/8 23:00',                '2009/7/9 0:00', '2009/7/9 1:00', '2009/7/9 2:00', '2009/7/9 3:00', '2009/7/9 4:00', '2009/7/9 5:00', '2009/7/9 6:00', '2009/7/9 7:00', '2009/7/9 8:00', '2009/7/9 9:00', '2009/7/9 10:00', '2009/7/9 11:00', '2009/7/9 12:00', '2009/7/9 13:00', '2009/7/9 14:00', '2009/7/9 15:00', '2009/7/9 16:00', '2009/7/9 17:00', '2009/7/9 18:00', '2009/7/9 19:00', '2009/7/9 20:00', '2009/7/9 21:00', '2009/7/9 22:00', '2009/7/9 23:00',                '2009/7/10 0:00', '2009/7/10 1:00', '2009/7/10 2:00', '2009/7/10 3:00', '2009/7/10 4:00', '2009/7/10 5:00', '2009/7/10 6:00', '2009/7/10 7:00', '2009/7/10 8:00', '2009/7/10 9:00', '2009/7/10 10:00', '2009/7/10 11:00', '2009/7/10 12:00', '2009/7/10 13:00', '2009/7/10 14:00', '2009/7/10 15:00', '2009/7/10 16:00', '2009/7/10 17:00', '2009/7/10 18:00', '2009/7/10 19:00', '2009/7/10 20:00', '2009/7/10 21:00', '2009/7/10 22:00', '2009/7/10 23:00',                '2009/7/11 0:00', '2009/7/11 1:00', '2009/7/11 2:00', '2009/7/11 3:00', '2009/7/11 4:00', '2009/7/11 5:00', '2009/7/11 6:00', '2009/7/11 7:00', '2009/7/11 8:00', '2009/7/11 9:00', '2009/7/11 10:00', '2009/7/11 11:00', '2009/7/11 12:00', '2009/7/11 13:00', '2009/7/11 14:00', '2009/7/11 15:00', '2009/7/11 16:00', '2009/7/11 17:00', '2009/7/11 18:00', '2009/7/11 19:00', '2009/7/11 20:00', '2009/7/11 21:00', '2009/7/11 22:00', '2009/7/11 23:00',                '2009/7/12 0:00', '2009/7/12 1:00', '2009/7/12 2:00', '2009/7/12 3:00', '2009/7/12 4:00', '2009/7/12 5:00', '2009/7/12 6:00', '2009/7/12 7:00', '2009/7/12 8:00', '2009/7/12 9:00', '2009/7/12 10:00', '2009/7/12 11:00', '2009/7/12 12:00', '2009/7/12 13:00', '2009/7/12 14:00', '2009/7/12 15:00', '2009/7/12 16:00', '2009/7/12 17:00', '2009/7/12 18:00', '2009/7/12 19:00', '2009/7/12 20:00', '2009/7/12 21:00', '2009/7/12 22:00', '2009/7/12 23:00',                '2009/7/13 0:00', '2009/7/13 1:00', '2009/7/13 2:00', '2009/7/13 3:00', '2009/7/13 4:00', '2009/7/13 5:00', '2009/7/13 6:00', '2009/7/13 7:00', '2009/7/13 8:00', '2009/7/13 9:00', '2009/7/13 10:00', '2009/7/13 11:00', '2009/7/13 12:00', '2009/7/13 13:00', '2009/7/13 14:00', '2009/7/13 15:00', '2009/7/13 16:00', '2009/7/13 17:00', '2009/7/13 18:00', '2009/7/13 19:00', '2009/7/13 20:00', '2009/7/13 21:00', '2009/7/13 22:00', '2009/7/13 23:00',                '2009/7/14 0:00', '2009/7/14 1:00', '2009/7/14 2:00', '2009/7/14 3:00', '2009/7/14 4:00', '2009/7/14 5:00', '2009/7/14 6:00', '2009/7/14 7:00', '2009/7/14 8:00', '2009/7/14 9:00', '2009/7/14 10:00', '2009/7/14 11:00', '2009/7/14 12:00', '2009/7/14 13:00', '2009/7/14 14:00', '2009/7/14 15:00', '2009/7/14 16:00', '2009/7/14 17:00', '2009/7/14 18:00', '2009/7/14 19:00', '2009/7/14 20:00', '2009/7/14 21:00', '2009/7/14 22:00', '2009/7/14 23:00',                '2009/7/15 0:00', '2009/7/15 1:00', '2009/7/15 2:00', '2009/7/15 3:00', '2009/7/15 4:00', '2009/7/15 5:00', '2009/7/15 6:00', '2009/7/15 7:00', '2009/7/15 8:00', '2009/7/15 9:00', '2009/7/15 10:00', '2009/7/15 11:00', '2009/7/15 12:00', '2009/7/15 13:00', '2009/7/15 14:00', '2009/7/15 15:00', '2009/7/15 16:00', '2009/7/15 17:00', '2009/7/15 18:00', '2009/7/15 19:00', '2009/7/15 20:00', '2009/7/15 21:00', '2009/7/15 22:00', '2009/7/15 23:00',                '2009/7/16 0:00', '2009/7/16 1:00', '2009/7/16 2:00', '2009/7/16 3:00', '2009/7/16 4:00', '2009/7/16 5:00', '2009/7/16 6:00', '2009/7/16 7:00', '2009/7/16 8:00', '2009/7/16 9:00', '2009/7/16 10:00', '2009/7/16 11:00', '2009/7/16 12:00', '2009/7/16 13:00', '2009/7/16 14:00', '2009/7/16 15:00', '2009/7/16 16:00', '2009/7/16 17:00', '2009/7/16 18:00', '2009/7/16 19:00', '2009/7/16 20:00', '2009/7/16 21:00', '2009/7/16 22:00', '2009/7/16 23:00',                '2009/7/17 0:00', '2009/7/17 1:00', '2009/7/17 2:00', '2009/7/17 3:00', '2009/7/17 4:00', '2009/7/17 5:00', '2009/7/17 6:00', '2009/7/17 7:00', '2009/7/17 8:00', '2009/7/17 9:00', '2009/7/17 10:00', '2009/7/17 11:00', '2009/7/17 12:00', '2009/7/17 13:00', '2009/7/17 14:00', '2009/7/17 15:00', '2009/7/17 16:00', '2009/7/17 17:00', '2009/7/17 18:00', '2009/7/17 19:00', '2009/7/17 20:00', '2009/7/17 21:00', '2009/7/17 22:00', '2009/7/17 23:00',                '2009/7/18 0:00', '2009/7/18 1:00', '2009/7/18 2:00', '2009/7/18 3:00', '2009/7/18 4:00', '2009/7/18 5:00', '2009/7/18 6:00', '2009/7/18 7:00', '2009/7/18 8:00', '2009/7/18 9:00', '2009/7/18 10:00', '2009/7/18 11:00', '2009/7/18 12:00', '2009/7/18 13:00', '2009/7/18 14:00', '2009/7/18 15:00', '2009/7/18 16:00', '2009/7/18 17:00', '2009/7/18 18:00', '2009/7/18 19:00', '2009/7/18 20:00', '2009/7/18 21:00', '2009/7/18 22:00', '2009/7/18 23:00',                '2009/7/19 0:00', '2009/7/19 1:00', '2009/7/19 2:00', '2009/7/19 3:00', '2009/7/19 4:00', '2009/7/19 5:00', '2009/7/19 6:00', '2009/7/19 7:00', '2009/7/19 8:00', '2009/7/19 9:00', '2009/7/19 10:00', '2009/7/19 11:00', '2009/7/19 12:00', '2009/7/19 13:00', '2009/7/19 14:00', '2009/7/19 15:00', '2009/7/19 16:00', '2009/7/19 17:00', '2009/7/19 18:00', '2009/7/19 19:00', '2009/7/19 20:00', '2009/7/19 21:00', '2009/7/19 22:00', '2009/7/19 23:00',                '2009/7/20 0:00', '2009/7/20 1:00', '2009/7/20 2:00', '2009/7/20 3:00', '2009/7/20 4:00', '2009/7/20 5:00', '2009/7/20 6:00', '2009/7/20 7:00', '2009/7/20 8:00', '2009/7/20 9:00', '2009/7/20 10:00', '2009/7/20 11:00', '2009/7/20 12:00', '2009/7/20 13:00', '2009/7/20 14:00', '2009/7/20 15:00', '2009/7/20 16:00', '2009/7/20 17:00', '2009/7/20 18:00', '2009/7/20 19:00', '2009/7/20 20:00', '2009/7/20 21:00', '2009/7/20 22:00', '2009/7/20 23:00',                '2009/7/21 0:00', '2009/7/21 1:00', '2009/7/21 2:00', '2009/7/21 3:00', '2009/7/21 4:00', '2009/7/21 5:00', '2009/7/21 6:00', '2009/7/21 7:00', '2009/7/21 8:00', '2009/7/21 9:00', '2009/7/21 10:00', '2009/7/21 11:00', '2009/7/21 12:00', '2009/7/21 13:00', '2009/7/21 14:00', '2009/7/21 15:00', '2009/7/21 16:00', '2009/7/21 17:00', '2009/7/21 18:00', '2009/7/21 19:00', '2009/7/21 20:00', '2009/7/21 21:00', '2009/7/21 22:00', '2009/7/21 23:00',                '2009/7/22 0:00', '2009/7/22 1:00', '2009/7/22 2:00', '2009/7/22 3:00', '2009/7/22 4:00', '2009/7/22 5:00', '2009/7/22 6:00', '2009/7/22 7:00', '2009/7/22 8:00', '2009/7/22 9:00', '2009/7/22 10:00', '2009/7/22 11:00', '2009/7/22 12:00', '2009/7/22 13:00', '2009/7/22 14:00', '2009/7/22 15:00', '2009/7/22 16:00', '2009/7/22 17:00', '2009/7/22 18:00', '2009/7/22 19:00', '2009/7/22 20:00', '2009/7/22 21:00', '2009/7/22 22:00', '2009/7/22 23:00',                '2009/7/23 0:00', '2009/7/23 1:00', '2009/7/23 2:00', '2009/7/23 3:00', '2009/7/23 4:00', '2009/7/23 5:00', '2009/7/23 6:00', '2009/7/23 7:00', '2009/7/23 8:00', '2009/7/23 9:00', '2009/7/23 10:00', '2009/7/23 11:00', '2009/7/23 12:00', '2009/7/23 13:00', '2009/7/23 14:00', '2009/7/23 15:00', '2009/7/23 16:00', '2009/7/23 17:00', '2009/7/23 18:00', '2009/7/23 19:00', '2009/7/23 20:00', '2009/7/23 21:00', '2009/7/23 22:00', '2009/7/23 23:00',                '2009/7/24 0:00', '2009/7/24 1:00', '2009/7/24 2:00', '2009/7/24 3:00', '2009/7/24 4:00', '2009/7/24 5:00', '2009/7/24 6:00', '2009/7/24 7:00', '2009/7/24 8:00', '2009/7/24 9:00', '2009/7/24 10:00', '2009/7/24 11:00', '2009/7/24 12:00', '2009/7/24 13:00', '2009/7/24 14:00', '2009/7/24 15:00', '2009/7/24 16:00', '2009/7/24 17:00', '2009/7/24 18:00', '2009/7/24 19:00', '2009/7/24 20:00', '2009/7/24 21:00', '2009/7/24 22:00', '2009/7/24 23:00',                '2009/7/25 0:00', '2009/7/25 1:00', '2009/7/25 2:00', '2009/7/25 3:00', '2009/7/25 4:00', '2009/7/25 5:00', '2009/7/25 6:00', '2009/7/25 7:00', '2009/7/25 8:00', '2009/7/25 9:00', '2009/7/25 10:00', '2009/7/25 11:00', '2009/7/25 12:00', '2009/7/25 13:00', '2009/7/25 14:00', '2009/7/25 15:00', '2009/7/25 16:00', '2009/7/25 17:00', '2009/7/25 18:00', '2009/7/25 19:00', '2009/7/25 20:00', '2009/7/25 21:00', '2009/7/25 22:00', '2009/7/25 23:00',                '2009/7/26 0:00', '2009/7/26 1:00', '2009/7/26 2:00', '2009/7/26 3:00', '2009/7/26 4:00', '2009/7/26 5:00', '2009/7/26 6:00', '2009/7/26 7:00', '2009/7/26 8:00', '2009/7/26 9:00', '2009/7/26 10:00', '2009/7/26 11:00', '2009/7/26 12:00', '2009/7/26 13:00', '2009/7/26 14:00', '2009/7/26 15:00', '2009/7/26 16:00', '2009/7/26 17:00', '2009/7/26 18:00', '2009/7/26 19:00', '2009/7/26 20:00', '2009/7/26 21:00', '2009/7/26 22:00', '2009/7/26 23:00',                '2009/7/27 0:00', '2009/7/27 1:00', '2009/7/27 2:00', '2009/7/27 3:00', '2009/7/27 4:00', '2009/7/27 5:00', '2009/7/27 6:00', '2009/7/27 7:00', '2009/7/27 8:00', '2009/7/27 9:00', '2009/7/27 10:00', '2009/7/27 11:00', '2009/7/27 12:00', '2009/7/27 13:00', '2009/7/27 14:00', '2009/7/27 15:00', '2009/7/27 16:00', '2009/7/27 17:00', '2009/7/27 18:00', '2009/7/27 19:00', '2009/7/27 20:00', '2009/7/27 21:00', '2009/7/27 22:00', '2009/7/27 23:00',                '2009/7/28 0:00', '2009/7/28 1:00', '2009/7/28 2:00', '2009/7/28 3:00', '2009/7/28 4:00', '2009/7/28 5:00', '2009/7/28 6:00', '2009/7/28 7:00', '2009/7/28 8:00', '2009/7/28 9:00', '2009/7/28 10:00', '2009/7/28 11:00', '2009/7/28 12:00', '2009/7/28 13:00', '2009/7/28 14:00', '2009/7/28 15:00', '2009/7/28 16:00', '2009/7/28 17:00', '2009/7/28 18:00', '2009/7/28 19:00', '2009/7/28 20:00', '2009/7/28 21:00', '2009/7/28 22:00', '2009/7/28 23:00',                '2009/7/29 0:00', '2009/7/29 1:00', '2009/7/29 2:00', '2009/7/29 3:00', '2009/7/29 4:00', '2009/7/29 5:00', '2009/7/29 6:00', '2009/7/29 7:00', '2009/7/29 8:00', '2009/7/29 9:00', '2009/7/29 10:00', '2009/7/29 11:00', '2009/7/29 12:00', '2009/7/29 13:00', '2009/7/29 14:00', '2009/7/29 15:00', '2009/7/29 16:00', '2009/7/29 17:00', '2009/7/29 18:00', '2009/7/29 19:00', '2009/7/29 20:00', '2009/7/29 21:00', '2009/7/29 22:00', '2009/7/29 23:00',                '2009/7/30 0:00', '2009/7/30 1:00', '2009/7/30 2:00', '2009/7/30 3:00', '2009/7/30 4:00', '2009/7/30 5:00', '2009/7/30 6:00', '2009/7/30 7:00', '2009/7/30 8:00', '2009/7/30 9:00', '2009/7/30 10:00', '2009/7/30 11:00', '2009/7/30 12:00', '2009/7/30 13:00', '2009/7/30 14:00', '2009/7/30 15:00', '2009/7/30 16:00', '2009/7/30 17:00', '2009/7/30 18:00', '2009/7/30 19:00', '2009/7/30 20:00', '2009/7/30 21:00', '2009/7/30 22:00', '2009/7/30 23:00',                '2009/7/31 0:00', '2009/7/31 1:00', '2009/7/31 2:00', '2009/7/31 3:00', '2009/7/31 4:00', '2009/7/31 5:00', '2009/7/31 6:00', '2009/7/31 7:00', '2009/7/31 8:00', '2009/7/31 9:00', '2009/7/31 10:00', '2009/7/31 11:00', '2009/7/31 12:00', '2009/7/31 13:00', '2009/7/31 14:00', '2009/7/31 15:00', '2009/7/31 16:00', '2009/7/31 17:00', '2009/7/31 18:00', '2009/7/31 19:00', '2009/7/31 20:00', '2009/7/31 21:00', '2009/7/31 22:00', '2009/7/31 23:00',                '2009/8/1 0:00', '2009/8/1 1:00', '2009/8/1 2:00', '2009/8/1 3:00', '2009/8/1 4:00', '2009/8/1 5:00', '2009/8/1 6:00', '2009/8/1 7:00', '2009/8/1 8:00', '2009/8/1 9:00', '2009/8/1 10:00', '2009/8/1 11:00', '2009/8/1 12:00', '2009/8/1 13:00', '2009/8/1 14:00', '2009/8/1 15:00', '2009/8/1 16:00', '2009/8/1 17:00', '2009/8/1 18:00', '2009/8/1 19:00', '2009/8/1 20:00', '2009/8/1 21:00', '2009/8/1 22:00', '2009/8/1 23:00', '2009/8/2 0:00', '2009/8/2 1:00', '2009/8/2 2:00', '2009/8/2 3:00', '2009/8/2 4:00', '2009/8/2 5:00', '2009/8/2 6:00', '2009/8/2 7:00', '2009/8/2 8:00', '2009/8/2 9:00', '2009/8/2 10:00', '2009/8/2 11:00', '2009/8/2 12:00', '2009/8/2 13:00', '2009/8/2 14:00', '2009/8/2 15:00', '2009/8/2 16:00', '2009/8/2 17:00', '2009/8/2 18:00', '2009/8/2 19:00', '2009/8/2 20:00', '2009/8/2 21:00', '2009/8/2 22:00', '2009/8/2 23:00', '2009/8/3 0:00', '2009/8/3 1:00', '2009/8/3 2:00', '2009/8/3 3:00', '2009/8/3 4:00', '2009/8/3 5:00', '2009/8/3 6:00', '2009/8/3 7:00', '2009/8/3 8:00', '2009/8/3 9:00', '2009/8/3 10:00', '2009/8/3 11:00', '2009/8/3 12:00', '2009/8/3 13:00', '2009/8/3 14:00', '2009/8/3 15:00', '2009/8/3 16:00', '2009/8/3 17:00', '2009/8/3 18:00', '2009/8/3 19:00', '2009/8/3 20:00', '2009/8/3 21:00', '2009/8/3 22:00', '2009/8/3 23:00', '2009/8/4 0:00', '2009/8/4 1:00', '2009/8/4 2:00', '2009/8/4 3:00', '2009/8/4 4:00', '2009/8/4 5:00', '2009/8/4 6:00', '2009/8/4 7:00', '2009/8/4 8:00', '2009/8/4 9:00', '2009/8/4 10:00', '2009/8/4 11:00', '2009/8/4 12:00', '2009/8/4 13:00', '2009/8/4 14:00', '2009/8/4 15:00', '2009/8/4 16:00', '2009/8/4 17:00', '2009/8/4 18:00', '2009/8/4 19:00', '2009/8/4 20:00', '2009/8/4 21:00', '2009/8/4 22:00', '2009/8/4 23:00', '2009/8/5 0:00', '2009/8/5 1:00', '2009/8/5 2:00', '2009/8/5 3:00', '2009/8/5 4:00', '2009/8/5 5:00', '2009/8/5 6:00', '2009/8/5 7:00', '2009/8/5 8:00', '2009/8/5 9:00', '2009/8/5 10:00', '2009/8/5 11:00', '2009/8/5 12:00', '2009/8/5 13:00', '2009/8/5 14:00', '2009/8/5 15:00', '2009/8/5 16:00', '2009/8/5 17:00', '2009/8/5 18:00', '2009/8/5 19:00', '2009/8/5 20:00', '2009/8/5 21:00', '2009/8/5 22:00', '2009/8/5 23:00', '2009/8/6 0:00', '2009/8/6 1:00', '2009/8/6 2:00', '2009/8/6 3:00', '2009/8/6 4:00', '2009/8/6 5:00', '2009/8/6 6:00', '2009/8/6 7:00', '2009/8/6 8:00', '2009/8/6 9:00', '2009/8/6 10:00', '2009/8/6 11:00', '2009/8/6 12:00', '2009/8/6 13:00', '2009/8/6 14:00', '2009/8/6 15:00', '2009/8/6 16:00', '2009/8/6 17:00', '2009/8/6 18:00', '2009/8/6 19:00', '2009/8/6 20:00', '2009/8/6 21:00', '2009/8/6 22:00', '2009/8/6 23:00', '2009/8/7 0:00', '2009/8/7 1:00', '2009/8/7 2:00', '2009/8/7 3:00', '2009/8/7 4:00', '2009/8/7 5:00', '2009/8/7 6:00', '2009/8/7 7:00', '2009/8/7 8:00', '2009/8/7 9:00', '2009/8/7 10:00', '2009/8/7 11:00', '2009/8/7 12:00', '2009/8/7 13:00', '2009/8/7 14:00', '2009/8/7 15:00', '2009/8/7 16:00', '2009/8/7 17:00', '2009/8/7 18:00', '2009/8/7 19:00', '2009/8/7 20:00', '2009/8/7 21:00', '2009/8/7 22:00', '2009/8/7 23:00', '2009/8/8 0:00', '2009/8/8 1:00', '2009/8/8 2:00', '2009/8/8 3:00', '2009/8/8 4:00', '2009/8/8 5:00', '2009/8/8 6:00', '2009/8/8 7:00', '2009/8/8 8:00', '2009/8/8 9:00', '2009/8/8 10:00', '2009/8/8 11:00', '2009/8/8 12:00', '2009/8/8 13:00', '2009/8/8 14:00', '2009/8/8 15:00', '2009/8/8 16:00', '2009/8/8 17:00', '2009/8/8 18:00', '2009/8/8 19:00', '2009/8/8 20:00', '2009/8/8 21:00', '2009/8/8 22:00', '2009/8/8 23:00', '2009/8/9 0:00', '2009/8/9 1:00', '2009/8/9 2:00', '2009/8/9 3:00', '2009/8/9 4:00', '2009/8/9 5:00', '2009/8/9 6:00', '2009/8/9 7:00', '2009/8/9 8:00', '2009/8/9 9:00', '2009/8/9 10:00', '2009/8/9 11:00', '2009/8/9 12:00', '2009/8/9 13:00', '2009/8/9 14:00', '2009/8/9 15:00', '2009/8/9 16:00', '2009/8/9 17:00', '2009/8/9 18:00', '2009/8/9 19:00', '2009/8/9 20:00', '2009/8/9 21:00', '2009/8/9 22:00', '2009/8/9 23:00', '2009/8/10 0:00', '2009/8/10 1:00', '2009/8/10 2:00', '2009/8/10 3:00', '2009/8/10 4:00', '2009/8/10 5:00', '2009/8/10 6:00', '2009/8/10 7:00', '2009/8/10 8:00', '2009/8/10 9:00', '2009/8/10 10:00', '2009/8/10 11:00', '2009/8/10 12:00', '2009/8/10 13:00', '2009/8/10 14:00', '2009/8/10 15:00', '2009/8/10 16:00', '2009/8/10 17:00', '2009/8/10 18:00', '2009/8/10 19:00', '2009/8/10 20:00', '2009/8/10 21:00', '2009/8/10 22:00', '2009/8/10 23:00', '2009/8/11 0:00', '2009/8/11 1:00', '2009/8/11 2:00', '2009/8/11 3:00', '2009/8/11 4:00', '2009/8/11 5:00', '2009/8/11 6:00', '2009/8/11 7:00', '2009/8/11 8:00', '2009/8/11 9:00', '2009/8/11 10:00', '2009/8/11 11:00', '2009/8/11 12:00', '2009/8/11 13:00', '2009/8/11 14:00', '2009/8/11 15:00', '2009/8/11 16:00', '2009/8/11 17:00', '2009/8/11 18:00', '2009/8/11 19:00', '2009/8/11 20:00', '2009/8/11 21:00', '2009/8/11 22:00', '2009/8/11 23:00', '2009/8/12 0:00', '2009/8/12 1:00', '2009/8/12 2:00', '2009/8/12 3:00', '2009/8/12 4:00', '2009/8/12 5:00', '2009/8/12 6:00', '2009/8/12 7:00', '2009/8/12 8:00', '2009/8/12 9:00', '2009/8/12 10:00', '2009/8/12 11:00', '2009/8/12 12:00', '2009/8/12 13:00', '2009/8/12 14:00', '2009/8/12 15:00', '2009/8/12 16:00', '2009/8/12 17:00', '2009/8/12 18:00', '2009/8/12 19:00', '2009/8/12 20:00', '2009/8/12 21:00', '2009/8/12 22:00', '2009/8/12 23:00', '2009/8/13 0:00', '2009/8/13 1:00', '2009/8/13 2:00', '2009/8/13 3:00', '2009/8/13 4:00', '2009/8/13 5:00', '2009/8/13 6:00', '2009/8/13 7:00', '2009/8/13 8:00', '2009/8/13 9:00', '2009/8/13 10:00', '2009/8/13 11:00', '2009/8/13 12:00', '2009/8/13 13:00', '2009/8/13 14:00', '2009/8/13 15:00', '2009/8/13 16:00', '2009/8/13 17:00', '2009/8/13 18:00', '2009/8/13 19:00', '2009/8/13 20:00', '2009/8/13 21:00', '2009/8/13 22:00', '2009/8/13 23:00', '2009/8/14 0:00', '2009/8/14 1:00', '2009/8/14 2:00', '2009/8/14 3:00', '2009/8/14 4:00', '2009/8/14 5:00', '2009/8/14 6:00', '2009/8/14 7:00', '2009/8/14 8:00', '2009/8/14 9:00', '2009/8/14 10:00', '2009/8/14 11:00', '2009/8/14 12:00', '2009/8/14 13:00', '2009/8/14 14:00', '2009/8/14 15:00', '2009/8/14 16:00', '2009/8/14 17:00', '2009/8/14 18:00', '2009/8/14 19:00', '2009/8/14 20:00', '2009/8/14 21:00', '2009/8/14 22:00', '2009/8/14 23:00', '2009/8/15 0:00', '2009/8/15 1:00', '2009/8/15 2:00', '2009/8/15 3:00', '2009/8/15 4:00', '2009/8/15 5:00', '2009/8/15 6:00', '2009/8/15 7:00', '2009/8/15 8:00', '2009/8/15 9:00', '2009/8/15 10:00', '2009/8/15 11:00', '2009/8/15 12:00', '2009/8/15 13:00', '2009/8/15 14:00', '2009/8/15 15:00', '2009/8/15 16:00', '2009/8/15 17:00', '2009/8/15 18:00', '2009/8/15 19:00', '2009/8/15 20:00', '2009/8/15 21:00', '2009/8/15 22:00', '2009/8/15 23:00', '2009/8/16 0:00', '2009/8/16 1:00', '2009/8/16 2:00', '2009/8/16 3:00', '2009/8/16 4:00', '2009/8/16 5:00', '2009/8/16 6:00', '2009/8/16 7:00', '2009/8/16 8:00', '2009/8/16 9:00', '2009/8/16 10:00', '2009/8/16 11:00', '2009/8/16 12:00', '2009/8/16 13:00', '2009/8/16 14:00', '2009/8/16 15:00', '2009/8/16 16:00', '2009/8/16 17:00', '2009/8/16 18:00', '2009/8/16 19:00', '2009/8/16 20:00', '2009/8/16 21:00', '2009/8/16 22:00', '2009/8/16 23:00', '2009/8/17 0:00', '2009/8/17 1:00', '2009/8/17 2:00', '2009/8/17 3:00', '2009/8/17 4:00', '2009/8/17 5:00', '2009/8/17 6:00', '2009/8/17 7:00', '2009/8/17 8:00', '2009/8/17 9:00', '2009/8/17 10:00', '2009/8/17 11:00', '2009/8/17 12:00', '2009/8/17 13:00', '2009/8/17 14:00', '2009/8/17 15:00', '2009/8/17 16:00', '2009/8/17 17:00', '2009/8/17 18:00', '2009/8/17 19:00', '2009/8/17 20:00', '2009/8/17 21:00', '2009/8/17 22:00', '2009/8/17 23:00', '2009/8/18 0:00', '2009/8/18 1:00', '2009/8/18 2:00', '2009/8/18 3:00', '2009/8/18 4:00', '2009/8/18 5:00', '2009/8/18 6:00', '2009/8/18 7:00', '2009/8/18 8:00', '2009/8/18 9:00', '2009/8/18 10:00', '2009/8/18 11:00', '2009/8/18 12:00', '2009/8/18 13:00', '2009/8/18 14:00', '2009/8/18 15:00', '2009/8/18 16:00', '2009/8/18 17:00', '2009/8/18 18:00', '2009/8/18 19:00', '2009/8/18 20:00', '2009/8/18 21:00', '2009/8/18 22:00', '2009/8/18 23:00', '2009/8/19 0:00', '2009/8/19 1:00', '2009/8/19 2:00', '2009/8/19 3:00', '2009/8/19 4:00', '2009/8/19 5:00', '2009/8/19 6:00', '2009/8/19 7:00', '2009/8/19 8:00', '2009/8/19 9:00', '2009/8/19 10:00', '2009/8/19 11:00', '2009/8/19 12:00', '2009/8/19 13:00', '2009/8/19 14:00', '2009/8/19 15:00', '2009/8/19 16:00', '2009/8/19 17:00', '2009/8/19 18:00', '2009/8/19 19:00', '2009/8/19 20:00', '2009/8/19 21:00', '2009/8/19 22:00', '2009/8/19 23:00', '2009/8/20 0:00', '2009/8/20 1:00', '2009/8/20 2:00', '2009/8/20 3:00', '2009/8/20 4:00', '2009/8/20 5:00', '2009/8/20 6:00', '2009/8/20 7:00', '2009/8/20 8:00', '2009/8/20 9:00', '2009/8/20 10:00', '2009/8/20 11:00', '2009/8/20 12:00', '2009/8/20 13:00', '2009/8/20 14:00', '2009/8/20 15:00', '2009/8/20 16:00', '2009/8/20 17:00', '2009/8/20 18:00', '2009/8/20 19:00', '2009/8/20 20:00', '2009/8/20 21:00', '2009/8/20 22:00', '2009/8/20 23:00', '2009/8/21 0:00', '2009/8/21 1:00', '2009/8/21 2:00', '2009/8/21 3:00', '2009/8/21 4:00', '2009/8/21 5:00', '2009/8/21 6:00', '2009/8/21 7:00', '2009/8/21 8:00', '2009/8/21 9:00', '2009/8/21 10:00', '2009/8/21 11:00', '2009/8/21 12:00', '2009/8/21 13:00', '2009/8/21 14:00', '2009/8/21 15:00', '2009/8/21 16:00', '2009/8/21 17:00', '2009/8/21 18:00', '2009/8/21 19:00', '2009/8/21 20:00', '2009/8/21 21:00', '2009/8/21 22:00', '2009/8/21 23:00', '2009/8/22 0:00', '2009/8/22 1:00', '2009/8/22 2:00', '2009/8/22 3:00', '2009/8/22 4:00', '2009/8/22 5:00', '2009/8/22 6:00', '2009/8/22 7:00', '2009/8/22 8:00', '2009/8/22 9:00', '2009/8/22 10:00', '2009/8/22 11:00', '2009/8/22 12:00', '2009/8/22 13:00', '2009/8/22 14:00', '2009/8/22 15:00', '2009/8/22 16:00', '2009/8/22 17:00', '2009/8/22 18:00', '2009/8/22 19:00', '2009/8/22 20:00', '2009/8/22 21:00', '2009/8/22 22:00', '2009/8/22 23:00', '2009/8/23 0:00', '2009/8/23 1:00', '2009/8/23 2:00', '2009/8/23 3:00', '2009/8/23 4:00', '2009/8/23 5:00', '2009/8/23 6:00', '2009/8/23 7:00', '2009/8/23 8:00', '2009/8/23 9:00', '2009/8/23 10:00', '2009/8/23 11:00', '2009/8/23 12:00', '2009/8/23 13:00', '2009/8/23 14:00', '2009/8/23 15:00', '2009/8/23 16:00', '2009/8/23 17:00', '2009/8/23 18:00', '2009/8/23 19:00', '2009/8/23 20:00', '2009/8/23 21:00', '2009/8/23 22:00', '2009/8/23 23:00', '2009/8/24 0:00', '2009/8/24 1:00', '2009/8/24 2:00', '2009/8/24 3:00', '2009/8/24 4:00', '2009/8/24 5:00', '2009/8/24 6:00', '2009/8/24 7:00', '2009/8/24 8:00', '2009/8/24 9:00', '2009/8/24 10:00', '2009/8/24 11:00', '2009/8/24 12:00', '2009/8/24 13:00', '2009/8/24 14:00', '2009/8/24 15:00', '2009/8/24 16:00', '2009/8/24 17:00', '2009/8/24 18:00', '2009/8/24 19:00', '2009/8/24 20:00', '2009/8/24 21:00', '2009/8/24 22:00', '2009/8/24 23:00', '2009/8/25 0:00', '2009/8/25 1:00', '2009/8/25 2:00', '2009/8/25 3:00', '2009/8/25 4:00', '2009/8/25 5:00', '2009/8/25 6:00', '2009/8/25 7:00', '2009/8/25 8:00', '2009/8/25 9:00', '2009/8/25 10:00', '2009/8/25 11:00', '2009/8/25 12:00', '2009/8/25 13:00', '2009/8/25 14:00', '2009/8/25 15:00', '2009/8/25 16:00', '2009/8/25 17:00', '2009/8/25 18:00', '2009/8/25 19:00', '2009/8/25 20:00', '2009/8/25 21:00', '2009/8/25 22:00', '2009/8/25 23:00', '2009/8/26 0:00', '2009/8/26 1:00', '2009/8/26 2:00', '2009/8/26 3:00', '2009/8/26 4:00', '2009/8/26 5:00', '2009/8/26 6:00', '2009/8/26 7:00', '2009/8/26 8:00', '2009/8/26 9:00', '2009/8/26 10:00', '2009/8/26 11:00', '2009/8/26 12:00', '2009/8/26 13:00', '2009/8/26 14:00', '2009/8/26 15:00', '2009/8/26 16:00', '2009/8/26 17:00', '2009/8/26 18:00', '2009/8/26 19:00', '2009/8/26 20:00', '2009/8/26 21:00', '2009/8/26 22:00', '2009/8/26 23:00', '2009/8/27 0:00', '2009/8/27 1:00', '2009/8/27 2:00', '2009/8/27 3:00', '2009/8/27 4:00', '2009/8/27 5:00', '2009/8/27 6:00', '2009/8/27 7:00', '2009/8/27 8:00', '2009/8/27 9:00', '2009/8/27 10:00', '2009/8/27 11:00', '2009/8/27 12:00', '2009/8/27 13:00', '2009/8/27 14:00', '2009/8/27 15:00', '2009/8/27 16:00', '2009/8/27 17:00', '2009/8/27 18:00', '2009/8/27 19:00', '2009/8/27 20:00', '2009/8/27 21:00', '2009/8/27 22:00', '2009/8/27 23:00', '2009/8/28 0:00', '2009/8/28 1:00', '2009/8/28 2:00', '2009/8/28 3:00', '2009/8/28 4:00', '2009/8/28 5:00', '2009/8/28 6:00', '2009/8/28 7:00', '2009/8/28 8:00', '2009/8/28 9:00', '2009/8/28 10:00', '2009/8/28 11:00', '2009/8/28 12:00', '2009/8/28 13:00', '2009/8/28 14:00', '2009/8/28 15:00', '2009/8/28 16:00', '2009/8/28 17:00', '2009/8/28 18:00', '2009/8/28 19:00', '2009/8/28 20:00', '2009/8/28 21:00', '2009/8/28 22:00', '2009/8/28 23:00', '2009/8/29 0:00', '2009/8/29 1:00', '2009/8/29 2:00', '2009/8/29 3:00', '2009/8/29 4:00', '2009/8/29 5:00', '2009/8/29 6:00', '2009/8/29 7:00', '2009/8/29 8:00', '2009/8/29 9:00', '2009/8/29 10:00', '2009/8/29 11:00', '2009/8/29 12:00', '2009/8/29 13:00', '2009/8/29 14:00', '2009/8/29 15:00', '2009/8/29 16:00', '2009/8/29 17:00', '2009/8/29 18:00', '2009/8/29 19:00', '2009/8/29 20:00', '2009/8/29 21:00', '2009/8/29 22:00', '2009/8/29 23:00', '2009/8/30 0:00', '2009/8/30 1:00', '2009/8/30 2:00', '2009/8/30 3:00', '2009/8/30 4:00', '2009/8/30 5:00', '2009/8/30 6:00', '2009/8/30 7:00', '2009/8/30 8:00', '2009/8/30 9:00', '2009/8/30 10:00', '2009/8/30 11:00', '2009/8/30 12:00', '2009/8/30 13:00', '2009/8/30 14:00', '2009/8/30 15:00', '2009/8/30 16:00', '2009/8/30 17:00', '2009/8/30 18:00', '2009/8/30 19:00', '2009/8/30 20:00', '2009/8/30 21:00', '2009/8/30 22:00', '2009/8/30 23:00', '2009/8/31 0:00', '2009/8/31 1:00', '2009/8/31 2:00', '2009/8/31 3:00', '2009/8/31 4:00', '2009/8/31 5:00', '2009/8/31 6:00', '2009/8/31 7:00', '2009/8/31 8:00', '2009/8/31 9:00', '2009/8/31 10:00', '2009/8/31 11:00', '2009/8/31 12:00', '2009/8/31 13:00', '2009/8/31 14:00', '2009/8/31 15:00', '2009/8/31 16:00', '2009/8/31 17:00', '2009/8/31 18:00', '2009/8/31 19:00', '2009/8/31 20:00', '2009/8/31 21:00', '2009/8/31 22:00', '2009/8/31 23:00',                '2009/9/1 0:00', '2009/9/1 1:00', '2009/9/1 2:00', '2009/9/1 3:00', '2009/9/1 4:00', '2009/9/1 5:00', '2009/9/1 6:00', '2009/9/1 7:00', '2009/9/1 8:00', '2009/9/1 9:00', '2009/9/1 10:00', '2009/9/1 11:00', '2009/9/1 12:00', '2009/9/1 13:00', '2009/9/1 14:00', '2009/9/1 15:00', '2009/9/1 16:00', '2009/9/1 17:00', '2009/9/1 18:00', '2009/9/1 19:00', '2009/9/1 20:00', '2009/9/1 21:00', '2009/9/1 22:00', '2009/9/1 23:00', '2009/9/2 0:00', '2009/9/2 1:00', '2009/9/2 2:00', '2009/9/2 3:00', '2009/9/2 4:00', '2009/9/2 5:00', '2009/9/2 6:00', '2009/9/2 7:00', '2009/9/2 8:00', '2009/9/2 9:00', '2009/9/2 10:00', '2009/9/2 11:00', '2009/9/2 12:00', '2009/9/2 13:00', '2009/9/2 14:00', '2009/9/2 15:00', '2009/9/2 16:00', '2009/9/2 17:00', '2009/9/2 18:00', '2009/9/2 19:00', '2009/9/2 20:00', '2009/9/2 21:00', '2009/9/2 22:00', '2009/9/2 23:00', '2009/9/3 0:00', '2009/9/3 1:00', '2009/9/3 2:00', '2009/9/3 3:00', '2009/9/3 4:00', '2009/9/3 5:00', '2009/9/3 6:00', '2009/9/3 7:00', '2009/9/3 8:00', '2009/9/3 9:00', '2009/9/3 10:00', '2009/9/3 11:00', '2009/9/3 12:00', '2009/9/3 13:00', '2009/9/3 14:00', '2009/9/3 15:00', '2009/9/3 16:00', '2009/9/3 17:00', '2009/9/3 18:00', '2009/9/3 19:00', '2009/9/3 20:00', '2009/9/3 21:00', '2009/9/3 22:00', '2009/9/3 23:00', '2009/9/4 0:00', '2009/9/4 1:00', '2009/9/4 2:00', '2009/9/4 3:00', '2009/9/4 4:00', '2009/9/4 5:00', '2009/9/4 6:00', '2009/9/4 7:00', '2009/9/4 8:00', '2009/9/4 9:00', '2009/9/4 10:00', '2009/9/4 11:00', '2009/9/4 12:00', '2009/9/4 13:00', '2009/9/4 14:00', '2009/9/4 15:00', '2009/9/4 16:00', '2009/9/4 17:00', '2009/9/4 18:00', '2009/9/4 19:00', '2009/9/4 20:00', '2009/9/4 21:00', '2009/9/4 22:00', '2009/9/4 23:00', '2009/9/5 0:00', '2009/9/5 1:00', '2009/9/5 2:00', '2009/9/5 3:00', '2009/9/5 4:00', '2009/9/5 5:00', '2009/9/5 6:00', '2009/9/5 7:00', '2009/9/5 8:00', '2009/9/5 9:00', '2009/9/5 10:00', '2009/9/5 11:00', '2009/9/5 12:00', '2009/9/5 13:00', '2009/9/5 14:00', '2009/9/5 15:00', '2009/9/5 16:00', '2009/9/5 17:00', '2009/9/5 18:00', '2009/9/5 19:00', '2009/9/5 20:00', '2009/9/5 21:00', '2009/9/5 22:00', '2009/9/5 23:00', '2009/9/6 0:00', '2009/9/6 1:00', '2009/9/6 2:00', '2009/9/6 3:00', '2009/9/6 4:00', '2009/9/6 5:00', '2009/9/6 6:00', '2009/9/6 7:00', '2009/9/6 8:00', '2009/9/6 9:00', '2009/9/6 10:00', '2009/9/6 11:00', '2009/9/6 12:00', '2009/9/6 13:00', '2009/9/6 14:00', '2009/9/6 15:00', '2009/9/6 16:00', '2009/9/6 17:00', '2009/9/6 18:00', '2009/9/6 19:00', '2009/9/6 20:00', '2009/9/6 21:00', '2009/9/6 22:00', '2009/9/6 23:00', '2009/9/7 0:00', '2009/9/7 1:00', '2009/9/7 2:00', '2009/9/7 3:00', '2009/9/7 4:00', '2009/9/7 5:00', '2009/9/7 6:00', '2009/9/7 7:00', '2009/9/7 8:00', '2009/9/7 9:00', '2009/9/7 10:00', '2009/9/7 11:00', '2009/9/7 12:00', '2009/9/7 13:00', '2009/9/7 14:00', '2009/9/7 15:00', '2009/9/7 16:00', '2009/9/7 17:00', '2009/9/7 18:00', '2009/9/7 19:00', '2009/9/7 20:00', '2009/9/7 21:00', '2009/9/7 22:00', '2009/9/7 23:00', '2009/9/8 0:00', '2009/9/8 1:00', '2009/9/8 2:00', '2009/9/8 3:00', '2009/9/8 4:00', '2009/9/8 5:00', '2009/9/8 6:00', '2009/9/8 7:00', '2009/9/8 8:00', '2009/9/8 9:00', '2009/9/8 10:00', '2009/9/8 11:00', '2009/9/8 12:00', '2009/9/8 13:00', '2009/9/8 14:00', '2009/9/8 15:00', '2009/9/8 16:00', '2009/9/8 17:00', '2009/9/8 18:00', '2009/9/8 19:00', '2009/9/8 20:00', '2009/9/8 21:00', '2009/9/8 22:00', '2009/9/8 23:00', '2009/9/9 0:00', '2009/9/9 1:00', '2009/9/9 2:00', '2009/9/9 3:00', '2009/9/9 4:00', '2009/9/9 5:00', '2009/9/9 6:00', '2009/9/9 7:00', '2009/9/9 8:00', '2009/9/9 9:00', '2009/9/9 10:00', '2009/9/9 11:00', '2009/9/9 12:00', '2009/9/9 13:00', '2009/9/9 14:00', '2009/9/9 15:00', '2009/9/9 16:00', '2009/9/9 17:00', '2009/9/9 18:00', '2009/9/9 19:00', '2009/9/9 20:00', '2009/9/9 21:00', '2009/9/9 22:00', '2009/9/9 23:00', '2009/9/10 0:00', '2009/9/10 1:00', '2009/9/10 2:00', '2009/9/10 3:00', '2009/9/10 4:00', '2009/9/10 5:00', '2009/9/10 6:00', '2009/9/10 7:00', '2009/9/10 8:00', '2009/9/10 9:00', '2009/9/10 10:00', '2009/9/10 11:00', '2009/9/10 12:00', '2009/9/10 13:00', '2009/9/10 14:00', '2009/9/10 15:00', '2009/9/10 16:00', '2009/9/10 17:00', '2009/9/10 18:00', '2009/9/10 19:00', '2009/9/10 20:00', '2009/9/10 21:00', '2009/9/10 22:00', '2009/9/10 23:00', '2009/9/11 0:00', '2009/9/11 1:00', '2009/9/11 2:00', '2009/9/11 3:00', '2009/9/11 4:00', '2009/9/11 5:00', '2009/9/11 6:00', '2009/9/11 7:00', '2009/9/11 8:00', '2009/9/11 9:00', '2009/9/11 10:00', '2009/9/11 11:00', '2009/9/11 12:00', '2009/9/11 13:00', '2009/9/11 14:00', '2009/9/11 15:00', '2009/9/11 16:00', '2009/9/11 17:00', '2009/9/11 18:00', '2009/9/11 19:00', '2009/9/11 20:00', '2009/9/11 21:00', '2009/9/11 22:00', '2009/9/11 23:00', '2009/9/12 0:00', '2009/9/12 1:00', '2009/9/12 2:00', '2009/9/12 3:00', '2009/9/12 4:00', '2009/9/12 5:00', '2009/9/12 6:00', '2009/9/12 7:00', '2009/9/12 8:00', '2009/9/12 9:00', '2009/9/12 10:00', '2009/9/12 11:00', '2009/9/12 12:00', '2009/9/12 13:00', '2009/9/12 14:00', '2009/9/12 15:00', '2009/9/12 16:00', '2009/9/12 17:00', '2009/9/12 18:00', '2009/9/12 19:00', '2009/9/12 20:00', '2009/9/12 21:00', '2009/9/12 22:00', '2009/9/12 23:00', '2009/9/13 0:00', '2009/9/13 1:00', '2009/9/13 2:00', '2009/9/13 3:00', '2009/9/13 4:00', '2009/9/13 5:00', '2009/9/13 6:00', '2009/9/13 7:00', '2009/9/13 8:00', '2009/9/13 9:00', '2009/9/13 10:00', '2009/9/13 11:00', '2009/9/13 12:00', '2009/9/13 13:00', '2009/9/13 14:00', '2009/9/13 15:00', '2009/9/13 16:00', '2009/9/13 17:00', '2009/9/13 18:00', '2009/9/13 19:00', '2009/9/13 20:00', '2009/9/13 21:00', '2009/9/13 22:00', '2009/9/13 23:00', '2009/9/14 0:00', '2009/9/14 1:00', '2009/9/14 2:00', '2009/9/14 3:00', '2009/9/14 4:00', '2009/9/14 5:00', '2009/9/14 6:00', '2009/9/14 7:00', '2009/9/14 8:00', '2009/9/14 9:00', '2009/9/14 10:00', '2009/9/14 11:00', '2009/9/14 12:00', '2009/9/14 13:00', '2009/9/14 14:00', '2009/9/14 15:00', '2009/9/14 16:00', '2009/9/14 17:00', '2009/9/14 18:00', '2009/9/14 19:00', '2009/9/14 20:00', '2009/9/14 21:00', '2009/9/14 22:00', '2009/9/14 23:00', '2009/9/15 0:00', '2009/9/15 1:00', '2009/9/15 2:00', '2009/9/15 3:00', '2009/9/15 4:00', '2009/9/15 5:00', '2009/9/15 6:00', '2009/9/15 7:00', '2009/9/15 8:00', '2009/9/15 9:00', '2009/9/15 10:00', '2009/9/15 11:00', '2009/9/15 12:00', '2009/9/15 13:00', '2009/9/15 14:00', '2009/9/15 15:00', '2009/9/15 16:00', '2009/9/15 17:00', '2009/9/15 18:00', '2009/9/15 19:00', '2009/9/15 20:00', '2009/9/15 21:00', '2009/9/15 22:00', '2009/9/15 23:00', '2009/9/16 0:00', '2009/9/16 1:00', '2009/9/16 2:00', '2009/9/16 3:00', '2009/9/16 4:00', '2009/9/16 5:00', '2009/9/16 6:00', '2009/9/16 7:00', '2009/9/16 8:00', '2009/9/16 9:00', '2009/9/16 10:00', '2009/9/16 11:00', '2009/9/16 12:00', '2009/9/16 13:00', '2009/9/16 14:00', '2009/9/16 15:00', '2009/9/16 16:00', '2009/9/16 17:00', '2009/9/16 18:00', '2009/9/16 19:00', '2009/9/16 20:00', '2009/9/16 21:00', '2009/9/16 22:00', '2009/9/16 23:00', '2009/9/17 0:00', '2009/9/17 1:00', '2009/9/17 2:00', '2009/9/17 3:00', '2009/9/17 4:00', '2009/9/17 5:00', '2009/9/17 6:00', '2009/9/17 7:00', '2009/9/17 8:00', '2009/9/17 9:00', '2009/9/17 10:00', '2009/9/17 11:00', '2009/9/17 12:00', '2009/9/17 13:00', '2009/9/17 14:00', '2009/9/17 15:00', '2009/9/17 16:00', '2009/9/17 17:00', '2009/9/17 18:00', '2009/9/17 19:00', '2009/9/17 20:00', '2009/9/17 21:00', '2009/9/17 22:00', '2009/9/17 23:00', '2009/9/18 0:00', '2009/9/18 1:00', '2009/9/18 2:00', '2009/9/18 3:00', '2009/9/18 4:00', '2009/9/18 5:00', '2009/9/18 6:00', '2009/9/18 7:00', '2009/9/18 8:00', '2009/9/18 9:00', '2009/9/18 10:00', '2009/9/18 11:00', '2009/9/18 12:00', '2009/9/18 13:00', '2009/9/18 14:00', '2009/9/18 15:00', '2009/9/18 16:00', '2009/9/18 17:00', '2009/9/18 18:00', '2009/9/18 19:00', '2009/9/18 20:00', '2009/9/18 21:00', '2009/9/18 22:00', '2009/9/18 23:00', '2009/9/19 0:00', '2009/9/19 1:00', '2009/9/19 2:00', '2009/9/19 3:00', '2009/9/19 4:00', '2009/9/19 5:00', '2009/9/19 6:00', '2009/9/19 7:00', '2009/9/19 8:00', '2009/9/19 9:00', '2009/9/19 10:00', '2009/9/19 11:00', '2009/9/19 12:00', '2009/9/19 13:00', '2009/9/19 14:00', '2009/9/19 15:00', '2009/9/19 16:00', '2009/9/19 17:00', '2009/9/19 18:00', '2009/9/19 19:00', '2009/9/19 20:00', '2009/9/19 21:00', '2009/9/19 22:00', '2009/9/19 23:00', '2009/9/20 0:00', '2009/9/20 1:00', '2009/9/20 2:00', '2009/9/20 3:00', '2009/9/20 4:00', '2009/9/20 5:00', '2009/9/20 6:00', '2009/9/20 7:00', '2009/9/20 8:00', '2009/9/20 9:00', '2009/9/20 10:00', '2009/9/20 11:00', '2009/9/20 12:00', '2009/9/20 13:00', '2009/9/20 14:00', '2009/9/20 15:00', '2009/9/20 16:00', '2009/9/20 17:00', '2009/9/20 18:00', '2009/9/20 19:00', '2009/9/20 20:00', '2009/9/20 21:00', '2009/9/20 22:00', '2009/9/20 23:00', '2009/9/21 0:00', '2009/9/21 1:00', '2009/9/21 2:00', '2009/9/21 3:00', '2009/9/21 4:00', '2009/9/21 5:00', '2009/9/21 6:00', '2009/9/21 7:00', '2009/9/21 8:00', '2009/9/21 9:00', '2009/9/21 10:00', '2009/9/21 11:00', '2009/9/21 12:00', '2009/9/21 13:00', '2009/9/21 14:00', '2009/9/21 15:00', '2009/9/21 16:00', '2009/9/21 17:00', '2009/9/21 18:00', '2009/9/21 19:00', '2009/9/21 20:00', '2009/9/21 21:00', '2009/9/21 22:00', '2009/9/21 23:00', '2009/9/22 0:00', '2009/9/22 1:00', '2009/9/22 2:00', '2009/9/22 3:00', '2009/9/22 4:00', '2009/9/22 5:00', '2009/9/22 6:00', '2009/9/22 7:00', '2009/9/22 8:00', '2009/9/22 9:00', '2009/9/22 10:00', '2009/9/22 11:00', '2009/9/22 12:00', '2009/9/22 13:00', '2009/9/22 14:00', '2009/9/22 15:00', '2009/9/22 16:00', '2009/9/22 17:00', '2009/9/22 18:00', '2009/9/22 19:00', '2009/9/22 20:00', '2009/9/22 21:00', '2009/9/22 22:00', '2009/9/22 23:00', '2009/9/23 0:00', '2009/9/23 1:00', '2009/9/23 2:00', '2009/9/23 3:00', '2009/9/23 4:00', '2009/9/23 5:00', '2009/9/23 6:00', '2009/9/23 7:00', '2009/9/23 8:00', '2009/9/23 9:00', '2009/9/23 10:00', '2009/9/23 11:00', '2009/9/23 12:00', '2009/9/23 13:00', '2009/9/23 14:00', '2009/9/23 15:00', '2009/9/23 16:00', '2009/9/23 17:00', '2009/9/23 18:00', '2009/9/23 19:00', '2009/9/23 20:00', '2009/9/23 21:00', '2009/9/23 22:00', '2009/9/23 23:00', '2009/9/24 0:00', '2009/9/24 1:00', '2009/9/24 2:00', '2009/9/24 3:00', '2009/9/24 4:00', '2009/9/24 5:00', '2009/9/24 6:00', '2009/9/24 7:00', '2009/9/24 8:00', '2009/9/24 9:00', '2009/9/24 10:00', '2009/9/24 11:00', '2009/9/24 12:00', '2009/9/24 13:00', '2009/9/24 14:00', '2009/9/24 15:00', '2009/9/24 16:00', '2009/9/24 17:00', '2009/9/24 18:00', '2009/9/24 19:00', '2009/9/24 20:00', '2009/9/24 21:00', '2009/9/24 22:00', '2009/9/24 23:00', '2009/9/25 0:00', '2009/9/25 1:00', '2009/9/25 2:00', '2009/9/25 3:00', '2009/9/25 4:00', '2009/9/25 5:00', '2009/9/25 6:00', '2009/9/25 7:00', '2009/9/25 8:00', '2009/9/25 9:00', '2009/9/25 10:00', '2009/9/25 11:00', '2009/9/25 12:00', '2009/9/25 13:00', '2009/9/25 14:00', '2009/9/25 15:00', '2009/9/25 16:00', '2009/9/25 17:00', '2009/9/25 18:00', '2009/9/25 19:00', '2009/9/25 20:00', '2009/9/25 21:00', '2009/9/25 22:00', '2009/9/25 23:00', '2009/9/26 0:00', '2009/9/26 1:00', '2009/9/26 2:00', '2009/9/26 3:00', '2009/9/26 4:00', '2009/9/26 5:00', '2009/9/26 6:00', '2009/9/26 7:00', '2009/9/26 8:00', '2009/9/26 9:00', '2009/9/26 10:00', '2009/9/26 11:00', '2009/9/26 12:00', '2009/9/26 13:00', '2009/9/26 14:00', '2009/9/26 15:00', '2009/9/26 16:00', '2009/9/26 17:00', '2009/9/26 18:00', '2009/9/26 19:00', '2009/9/26 20:00', '2009/9/26 21:00', '2009/9/26 22:00', '2009/9/26 23:00', '2009/9/27 0:00', '2009/9/27 1:00', '2009/9/27 2:00', '2009/9/27 3:00', '2009/9/27 4:00', '2009/9/27 5:00', '2009/9/27 6:00', '2009/9/27 7:00', '2009/9/27 8:00', '2009/9/27 9:00', '2009/9/27 10:00', '2009/9/27 11:00', '2009/9/27 12:00', '2009/9/27 13:00', '2009/9/27 14:00', '2009/9/27 15:00', '2009/9/27 16:00', '2009/9/27 17:00', '2009/9/27 18:00', '2009/9/27 19:00', '2009/9/27 20:00', '2009/9/27 21:00', '2009/9/27 22:00', '2009/9/27 23:00', '2009/9/28 0:00', '2009/9/28 1:00', '2009/9/28 2:00', '2009/9/28 3:00', '2009/9/28 4:00', '2009/9/28 5:00', '2009/9/28 6:00', '2009/9/28 7:00', '2009/9/28 8:00', '2009/9/28 9:00', '2009/9/28 10:00', '2009/9/28 11:00', '2009/9/28 12:00', '2009/9/28 13:00', '2009/9/28 14:00', '2009/9/28 15:00', '2009/9/28 16:00', '2009/9/28 17:00', '2009/9/28 18:00', '2009/9/28 19:00', '2009/9/28 20:00', '2009/9/28 21:00', '2009/9/28 22:00', '2009/9/28 23:00', '2009/9/29 0:00', '2009/9/29 1:00', '2009/9/29 2:00', '2009/9/29 3:00', '2009/9/29 4:00', '2009/9/29 5:00', '2009/9/29 6:00', '2009/9/29 7:00', '2009/9/29 8:00', '2009/9/29 9:00', '2009/9/29 10:00', '2009/9/29 11:00', '2009/9/29 12:00', '2009/9/29 13:00', '2009/9/29 14:00', '2009/9/29 15:00', '2009/9/29 16:00', '2009/9/29 17:00', '2009/9/29 18:00', '2009/9/29 19:00', '2009/9/29 20:00', '2009/9/29 21:00', '2009/9/29 22:00', '2009/9/29 23:00', '2009/9/30 0:00', '2009/9/30 1:00', '2009/9/30 2:00', '2009/9/30 3:00', '2009/9/30 4:00', '2009/9/30 5:00', '2009/9/30 6:00', '2009/9/30 7:00', '2009/9/30 8:00', '2009/9/30 9:00', '2009/9/30 10:00', '2009/9/30 11:00', '2009/9/30 12:00', '2009/9/30 13:00', '2009/9/30 14:00', '2009/9/30 15:00', '2009/9/30 16:00', '2009/9/30 17:00', '2009/9/30 18:00', '2009/9/30 19:00', '2009/9/30 20:00', '2009/9/30 21:00', '2009/9/30 22:00', '2009/9/30 23:00',                '2009/10/1 0:00', '2009/10/1 1:00', '2009/10/1 2:00', '2009/10/1 3:00', '2009/10/1 4:00', '2009/10/1 5:00', '2009/10/1 6:00', '2009/10/1 7:00', '2009/10/1 8:00', '2009/10/1 9:00', '2009/10/1 10:00', '2009/10/1 11:00', '2009/10/1 12:00', '2009/10/1 13:00', '2009/10/1 14:00', '2009/10/1 15:00', '2009/10/1 16:00', '2009/10/1 17:00', '2009/10/1 18:00', '2009/10/1 19:00', '2009/10/1 20:00', '2009/10/1 21:00', '2009/10/1 22:00', '2009/10/1 23:00', '2009/10/2 0:00', '2009/10/2 1:00', '2009/10/2 2:00', '2009/10/2 3:00', '2009/10/2 4:00', '2009/10/2 5:00', '2009/10/2 6:00', '2009/10/2 7:00', '2009/10/2 8:00', '2009/10/2 9:00', '2009/10/2 10:00', '2009/10/2 11:00', '2009/10/2 12:00', '2009/10/2 13:00', '2009/10/2 14:00', '2009/10/2 15:00', '2009/10/2 16:00', '2009/10/2 17:00', '2009/10/2 18:00', '2009/10/2 19:00', '2009/10/2 20:00', '2009/10/2 21:00', '2009/10/2 22:00', '2009/10/2 23:00', '2009/10/3 0:00', '2009/10/3 1:00', '2009/10/3 2:00', '2009/10/3 3:00', '2009/10/3 4:00', '2009/10/3 5:00', '2009/10/3 6:00', '2009/10/3 7:00', '2009/10/3 8:00', '2009/10/3 9:00', '2009/10/3 10:00', '2009/10/3 11:00', '2009/10/3 12:00', '2009/10/3 13:00', '2009/10/3 14:00', '2009/10/3 15:00', '2009/10/3 16:00', '2009/10/3 17:00', '2009/10/3 18:00', '2009/10/3 19:00', '2009/10/3 20:00', '2009/10/3 21:00', '2009/10/3 22:00', '2009/10/3 23:00', '2009/10/4 0:00', '2009/10/4 1:00', '2009/10/4 2:00', '2009/10/4 3:00', '2009/10/4 4:00', '2009/10/4 5:00', '2009/10/4 6:00', '2009/10/4 7:00', '2009/10/4 8:00', '2009/10/4 9:00', '2009/10/4 10:00', '2009/10/4 11:00', '2009/10/4 12:00', '2009/10/4 13:00', '2009/10/4 14:00', '2009/10/4 15:00', '2009/10/4 16:00', '2009/10/4 17:00', '2009/10/4 18:00', '2009/10/4 19:00', '2009/10/4 20:00', '2009/10/4 21:00', '2009/10/4 22:00', '2009/10/4 23:00', '2009/10/5 0:00', '2009/10/5 1:00', '2009/10/5 2:00', '2009/10/5 3:00', '2009/10/5 4:00', '2009/10/5 5:00', '2009/10/5 6:00', '2009/10/5 7:00', '2009/10/5 8:00', '2009/10/5 9:00', '2009/10/5 10:00', '2009/10/5 11:00', '2009/10/5 12:00', '2009/10/5 13:00', '2009/10/5 14:00', '2009/10/5 15:00', '2009/10/5 16:00', '2009/10/5 17:00', '2009/10/5 18:00', '2009/10/5 19:00', '2009/10/5 20:00', '2009/10/5 21:00', '2009/10/5 22:00', '2009/10/5 23:00', '2009/10/6 0:00', '2009/10/6 1:00', '2009/10/6 2:00', '2009/10/6 3:00', '2009/10/6 4:00', '2009/10/6 5:00', '2009/10/6 6:00', '2009/10/6 7:00', '2009/10/6 8:00', '2009/10/6 9:00', '2009/10/6 10:00', '2009/10/6 11:00', '2009/10/6 12:00', '2009/10/6 13:00', '2009/10/6 14:00', '2009/10/6 15:00', '2009/10/6 16:00', '2009/10/6 17:00', '2009/10/6 18:00', '2009/10/6 19:00', '2009/10/6 20:00', '2009/10/6 21:00', '2009/10/6 22:00', '2009/10/6 23:00', '2009/10/7 0:00', '2009/10/7 1:00', '2009/10/7 2:00', '2009/10/7 3:00', '2009/10/7 4:00', '2009/10/7 5:00', '2009/10/7 6:00', '2009/10/7 7:00', '2009/10/7 8:00', '2009/10/7 9:00', '2009/10/7 10:00', '2009/10/7 11:00', '2009/10/7 12:00', '2009/10/7 13:00', '2009/10/7 14:00', '2009/10/7 15:00', '2009/10/7 16:00', '2009/10/7 17:00', '2009/10/7 18:00', '2009/10/7 19:00', '2009/10/7 20:00', '2009/10/7 21:00', '2009/10/7 22:00', '2009/10/7 23:00', '2009/10/8 0:00', '2009/10/8 1:00', '2009/10/8 2:00', '2009/10/8 3:00', '2009/10/8 4:00', '2009/10/8 5:00', '2009/10/8 6:00', '2009/10/8 7:00', '2009/10/8 8:00', '2009/10/8 9:00', '2009/10/8 10:00', '2009/10/8 11:00', '2009/10/8 12:00', '2009/10/8 13:00', '2009/10/8 14:00', '2009/10/8 15:00', '2009/10/8 16:00', '2009/10/8 17:00', '2009/10/8 18:00', '2009/10/8 19:00', '2009/10/8 20:00', '2009/10/8 21:00', '2009/10/8 22:00', '2009/10/8 23:00', '2009/10/9 0:00', '2009/10/9 1:00', '2009/10/9 2:00', '2009/10/9 3:00', '2009/10/9 4:00', '2009/10/9 5:00', '2009/10/9 6:00', '2009/10/9 7:00', '2009/10/9 8:00', '2009/10/9 9:00', '2009/10/9 10:00', '2009/10/9 11:00', '2009/10/9 12:00', '2009/10/9 13:00', '2009/10/9 14:00', '2009/10/9 15:00', '2009/10/9 16:00', '2009/10/9 17:00', '2009/10/9 18:00', '2009/10/9 19:00', '2009/10/9 20:00', '2009/10/9 21:00', '2009/10/9 22:00', '2009/10/9 23:00', '2009/10/10 0:00', '2009/10/10 1:00', '2009/10/10 2:00', '2009/10/10 3:00', '2009/10/10 4:00', '2009/10/10 5:00', '2009/10/10 6:00', '2009/10/10 7:00', '2009/10/10 8:00', '2009/10/10 9:00', '2009/10/10 10:00', '2009/10/10 11:00', '2009/10/10 12:00', '2009/10/10 13:00', '2009/10/10 14:00', '2009/10/10 15:00', '2009/10/10 16:00', '2009/10/10 17:00', '2009/10/10 18:00', '2009/10/10 19:00', '2009/10/10 20:00', '2009/10/10 21:00', '2009/10/10 22:00', '2009/10/10 23:00', '2009/10/11 0:00', '2009/10/11 1:00', '2009/10/11 2:00', '2009/10/11 3:00', '2009/10/11 4:00', '2009/10/11 5:00', '2009/10/11 6:00', '2009/10/11 7:00', '2009/10/11 8:00', '2009/10/11 9:00', '2009/10/11 10:00', '2009/10/11 11:00', '2009/10/11 12:00', '2009/10/11 13:00', '2009/10/11 14:00', '2009/10/11 15:00', '2009/10/11 16:00', '2009/10/11 17:00', '2009/10/11 18:00', '2009/10/11 19:00', '2009/10/11 20:00', '2009/10/11 21:00', '2009/10/11 22:00', '2009/10/11 23:00', '2009/10/12 0:00', '2009/10/12 1:00', '2009/10/12 2:00', '2009/10/12 3:00', '2009/10/12 4:00', '2009/10/12 5:00', '2009/10/12 6:00', '2009/10/12 7:00', '2009/10/12 8:00', '2009/10/12 9:00', '2009/10/12 10:00', '2009/10/12 11:00', '2009/10/12 12:00', '2009/10/12 13:00', '2009/10/12 14:00', '2009/10/12 15:00', '2009/10/12 16:00', '2009/10/12 17:00', '2009/10/12 18:00', '2009/10/12 19:00', '2009/10/12 20:00', '2009/10/12 21:00', '2009/10/12 22:00', '2009/10/12 23:00', '2009/10/13 0:00', '2009/10/13 1:00', '2009/10/13 2:00', '2009/10/13 3:00', '2009/10/13 4:00', '2009/10/13 5:00', '2009/10/13 6:00', '2009/10/13 7:00', '2009/10/13 8:00', '2009/10/13 9:00', '2009/10/13 10:00', '2009/10/13 11:00', '2009/10/13 12:00', '2009/10/13 13:00', '2009/10/13 14:00', '2009/10/13 15:00', '2009/10/13 16:00', '2009/10/13 17:00', '2009/10/13 18:00', '2009/10/13 19:00', '2009/10/13 20:00', '2009/10/13 21:00', '2009/10/13 22:00', '2009/10/13 23:00', '2009/10/14 0:00', '2009/10/14 1:00', '2009/10/14 2:00', '2009/10/14 3:00', '2009/10/14 4:00', '2009/10/14 5:00', '2009/10/14 6:00', '2009/10/14 7:00', '2009/10/14 8:00', '2009/10/14 9:00', '2009/10/14 10:00', '2009/10/14 11:00', '2009/10/14 12:00', '2009/10/14 13:00', '2009/10/14 14:00', '2009/10/14 15:00', '2009/10/14 16:00', '2009/10/14 17:00', '2009/10/14 18:00', '2009/10/14 19:00', '2009/10/14 20:00', '2009/10/14 21:00', '2009/10/14 22:00', '2009/10/14 23:00', '2009/10/15 0:00', '2009/10/15 1:00', '2009/10/15 2:00', '2009/10/15 3:00', '2009/10/15 4:00', '2009/10/15 5:00', '2009/10/15 6:00', '2009/10/15 7:00', '2009/10/15 8:00', '2009/10/15 9:00', '2009/10/15 10:00', '2009/10/15 11:00', '2009/10/15 12:00', '2009/10/15 13:00', '2009/10/15 14:00', '2009/10/15 15:00', '2009/10/15 16:00', '2009/10/15 17:00', '2009/10/15 18:00', '2009/10/15 19:00', '2009/10/15 20:00', '2009/10/15 21:00', '2009/10/15 22:00', '2009/10/15 23:00', '2009/10/16 0:00', '2009/10/16 1:00', '2009/10/16 2:00', '2009/10/16 3:00', '2009/10/16 4:00', '2009/10/16 5:00', '2009/10/16 6:00', '2009/10/16 7:00', '2009/10/16 8:00', '2009/10/16 9:00', '2009/10/16 10:00', '2009/10/16 11:00', '2009/10/16 12:00', '2009/10/16 13:00', '2009/10/16 14:00', '2009/10/16 15:00', '2009/10/16 16:00', '2009/10/16 17:00', '2009/10/16 18:00', '2009/10/16 19:00', '2009/10/16 20:00', '2009/10/16 21:00', '2009/10/16 22:00', '2009/10/16 23:00', '2009/10/17 0:00', '2009/10/17 1:00', '2009/10/17 2:00', '2009/10/17 3:00', '2009/10/17 4:00', '2009/10/17 5:00', '2009/10/17 6:00', '2009/10/17 7:00', '2009/10/17 8:00', '2009/10/17 9:00', '2009/10/17 10:00', '2009/10/17 11:00', '2009/10/17 12:00', '2009/10/17 13:00', '2009/10/17 14:00', '2009/10/17 15:00', '2009/10/17 16:00', '2009/10/17 17:00', '2009/10/17 18:00', '2009/10/17 19:00', '2009/10/17 20:00', '2009/10/17 21:00', '2009/10/17 22:00', '2009/10/17 23:00', '2009/10/18 0:00', '2009/10/18 1:00', '2009/10/18 2:00', '2009/10/18 3:00', '2009/10/18 4:00', '2009/10/18 5:00', '2009/10/18 6:00', '2009/10/18 7:00', '2009/10/18 8:00'            ].map(function (str) {                return str.replace(' ', '\n');            })        }    ],    yAxis: [        {            name: '流量(m^3/s)',            type: 'value',            max: 500        },        {            name: '降雨量(mm)',            nameLocation: 'start',            max: 5,            type: 'value',            inverse: true        }    ],    series: [        {            name: '流量',            type: 'line',            animation: false,            areaStyle: {},            lineStyle: {                width: 1            },            markArea: {                silent: true,                data: [[{                    xAxis: '2009/9/12\n7:00'                }, {                    xAxis: '2009/9/22\n7:00'                }]]            },            data: [                0.97,0.96,0.96,0.95,0.95,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.93,0.92,0.91,0.9,0.89,0.88,0.87,0.87,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.87,0.88,0.9,0.93,0.96,0.99,1.03,1.06,1.1,1.14,1.17,1.2,1.23,1.26,1.29,1.33,1.36,1.4,1.43,1.45,1.48,1.49,1.51,1.51,1.5,1.49,1.47,1.44,1.41,1.37,1.34,1.3,1.27,1.24,1.22,1.2,1.19,1.18,1.16,1.15,1.14,1.13,1.12,1.11,1.11,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.1,1.09,1.09,1.08,1.07,1.06,1.05,1.04,1.03,1.03,1.02,1.01,1.01,1,0.99,0.98,0.97,0.96,0.96,0.95,0.95,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.93,0.92,0.91,0.9,0.89,0.88,0.87,0.87,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.85,0.84,0.83,0.82,0.81,0.8,0.8,0.79,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.77,0.75,0.73,0.71,0.68,0.65,0.63,0.61,0.59,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.57,0.57,0.57,0.56,0.55,0.55,0.54,0.54,0.53,0.52,0.52,0.51,0.51,0.5,0.5,0.49,0.48,0.48,0.47,0.47,0.47,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.46,0.52,0.67,0.9,1.19,1.52,1.87,2.22,2.55,2.84,3.07,3.22,3.28,3.28,3.28,3.28,3.28,3.28,3.28,3.28,3.28,3.28,3.28,3.28,3.28,3.24,3.13,2.97,2.77,2.54,2.3,2.05,1.82,1.62,1.46,1.35,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.31,1.3,1.26,1.21,1.14,1.06,0.97,0.89,0.81,0.74,0.69,0.65,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.63,0.63,0.62,0.62,0.61,0.6,0.59,0.59,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.59,0.61,0.63,0.65,0.68,0.71,0.73,0.75,0.77,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.77,0.75,0.73,0.71,0.68,0.65,0.63,0.61,0.59,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.58,0.59,0.59,0.6,0.61,0.62,0.62,0.63,0.63,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.65,0.66,0.68,0.69,0.71,0.73,0.74,0.76,0.77,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.79,0.81,0.82,0.84,0.86,0.88,0.9,0.92,0.93,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.93,0.92,0.91,0.9,0.89,0.88,0.87,0.87,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.86,0.85,0.84,0.82,0.8,0.78,0.76,0.75,0.73,0.72,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.72,0.73,0.74,0.76,0.78,0.79,0.82,0.84,0.86,0.89,0.91,0.94,0.97,1,1.02,1.05,1.08,1.11,1.14,1.17,1.19,1.22,1.25,1.27,1.29,1.31,1.33,1.35,1.36,1.38,1.39,1.39,1.4,1.4,1.4,1.39,1.37,1.35,1.32,1.29,1.26,1.22,1.18,1.14,1.1,1.05,1.01,0.97,0.93,0.89,0.85,0.82,0.78,0.76,0.74,0.72,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.72,0.73,0.74,0.75,0.77,0.78,0.8,0.82,0.84,0.87,0.89,0.92,0.94,0.97,0.99,1.02,1.05,1.08,1.1,1.13,1.16,1.18,1.21,1.23,1.26,1.28,1.3,1.32,1.34,1.35,1.37,1.38,1.39,1.4,1.41,1.41,1.42,1.42,1.43,1.43,1.43,1.44,1.44,1.44,1.44,1.45,1.45,1.45,1.46,1.46,1.46,1.47,1.47,1.48,1.48,1.49,1.5,1.51,1.54,1.62,1.73,1.88,2.05,2.24,2.45,2.67,2.89,3.11,3.31,3.51,3.69,3.86,4.03,4.18,4.33,4.48,4.62,4.76,4.89,5.02,5.16,5.29,5.43,5.57,5.71,5.86,6.02,6.18,6.36,6.54,6.73,6.93,7.15,7.38,7.62,7.88,8.16,8.46,8.77,9.11,9.46,9.84,10.24,10.67,11.12,11.6,12.3,13.66,16,38.43,82.21,146.6,218.7,226,225.23,223.08,219.78,212,199.82,184.6,168,151.65,137.21,126.31,119.94,115.52,112.06,108.92,105.44,101,94.56,86.36,77.67,69.76,63.9,60.38,57.41,54.84,52.57,50.56,48.71,46.97,45.25,43.48,41.6,39.5,37.19,34.81,32.46,30.27,28.36,26.85,25.86,25.5,25.5,25.5,25.5,25.5,25.5,25.5,25.5,25.5,25.5,25.5,25.5,25.5,25.27,24.65,23.7,22.52,21.17,19.75,18.33,16.98,15.8,14.85,14.23,14,14.02,14.08,14.17,14.29,14.44,14.61,14.8,15.01,15.23,15.47,15.71,15.95,16.19,16.43,16.67,16.89,17.1,17.29,17.46,17.61,17.73,17.82,17.88,17.9,17.63,16.88,15.75,14.33,12.71,10.98,9.23,7.56,6.05,4.81,3.92,3.47,3.28,3.1,2.93,2.76,2.61,2.46,2.32,2.19,2.07,1.96,1.85,1.75,1.66,1.58,1.51,1.44,1.39,1.34,1.29,1.26,1.23,1.22,1.2,1.2,1.2,1.2,1.2,1.2,1.21,1.21,1.21,1.21,1.22,1.22,1.22,1.23,1.23,1.23,1.24,1.24,1.25,1.25,1.25,1.26,1.26,1.27,1.27,1.27,1.28,1.28,1.28,1.29,1.29,1.29,1.29,1.3,1.3,1.3,1.3,1.3,1.3,1.3,1.3,1.3,1.3,1.3,1.29,1.29,1.29,1.29,1.28,1.28,1.28,1.27,1.27,1.26,1.25,1.25,1.24,1.23,1.23,1.22,1.21,1.2,1.16,1.06,0.95,0.83,0.74,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.71,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.69,0.69,0.69,0.69,0.69,0.69,0.69,0.69,0.68,0.68,0.68,0.68,0.68,0.68,0.67,0.67,0.67,0.67,0.67,0.67,0.67,0.66,0.66,0.66,0.66,0.66,0.66,0.66,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.65,0.66,0.68,0.69,0.71,0.73,0.74,0.76,0.77,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.78,0.8,0.86,0.95,1.08,1.25,1.46,1.7,1.97,2.28,2.63,3.01,3.42,3.87,4.35,4.86,5.4,5.98,6.59,7.92,10.49,14.04,18.31,23.04,27.98,32.87,37.45,41.46,44.64,46.74,47.5,46.86,45.16,42.77,40.04,37.33,35,32.74,30.21,27.7,25.5,23.9,23.2,23.06,22.94,22.84,22.77,22.72,22.7,22.8,23.23,23.95,24.91,26.04,27.3,28.76,30.7,33.39,37.12,42.15,48.77,65.22,252.1,257,237.32,221.19,212,208.67,206.89,205.2,202.15,189.82,172,165.3,160.49,156.8,153.44,149.62,144.6,138.27,131,123.11,114.9,106.69,98.79,91.5,85.13,80,75.53,71.03,66.65,62.54,58.85,55.73,53.31,51.75,51.2,56.53,68.25,80,91.01,102.03,109,112.37,115.29,117.68,119.48,120.61,121,119.45,115.57,110.52,105.47,101.58,100,99.97,99.94,99.92,99.9,99.88,99.86,99.85,99.84,99.83,99.82,99.81,99.81,99.8,99.8,99.8,122.15,163.65,186,182.96,175.15,164.56,153.18,143,136,131.37,126.98,122.81,118.85,115.09,111.52,108.13,104.9,101.83,98.9,96.11,93.44,90.87,88.41,86.04,83.74,81.51,79.33,77.2,75.1,73.02,70.95,68.88,66.8,64.87,63.14,61.4,59.53,57.67,56,54.6,53.36,52.2,51.05,49.85,48.5,46.87,44.92,42.74,40.42,38.04,35.69,33.46,31.44,29.72,28.38,27.51,27.2,27.2,27.2,27.2,27.2,27.2,27.2,27.2,27.2,27.2,27.2,27.2,27.2,27.14,26.97,26.7,26.35,25.95,25.49,25.02,24.53,24.04,23.58,23.16,22.8,22.46,22.11,21.75,21.39,21.03,20.69,20.36,20.05,19.78,19.54,19.35,19.2,19.09,19,18.92,18.85,18.79,18.74,18.68,18.62,18.56,18.49,18.4,18.3,18.17,18.02,17.83,17.63,17.41,17.18,16.93,16.68,16.43,16.18,15.93,15.7,15.47,15.22,14.97,14.71,14.45,14.18,13.93,13.68,13.44,13.21,13,12.8,12.62,12.46,12.31,12.16,12.03,11.89,11.76,11.62,11.48,11.33,11.17,11,10.81,10.59,10.36,10.12,9.86,9.61,9.36,9.12,8.89,8.68,8.5,8.35,8.21,8.08,7.94,7.81,7.68,7.56,7.46,7.36,7.29,7.23,7.19,7.18,7.51,8.42,9.81,11.58,13.63,15.86,18.16,20.44,22.58,24.49,26.06,27.2,28.08,28.95,29.81,30.65,31.48,32.28,33.07,33.82,34.55,35.25,35.92,36.56,37.15,37.71,38.23,38.7,39.13,39.5,39.83,40.1,40.31,40.47,40.57,40.6,40.49,40.16,39.64,38.94,38.09,37.1,36,34.79,33.51,32.17,30.79,29.39,27.99,26.6,25.25,23.96,22.75,21.63,20.63,19.76,19.04,18.49,18.14,18,17.97,17.95,17.94,17.92,17.91,17.9,17.89,17.88,17.87,17.85,17.83,17.8,17.7,17.46,17.13,16.7,16.21,15.68,15.13,14.57,14.04,13.56,13.14,12.8,12.52,12.27,12.02,11.79,11.57,11.37,11.16,10.97,10.78,10.59,10.39,10.2,10.01,9.81,9.63,9.44,9.26,9.08,8.9,8.73,8.56,8.39,8.22,8.06,7.9,7.73,7.57,7.41,7.25,7.09,6.94,6.79,6.65,6.52,6.4,6.28,6.17,6.08,5.98,5.9,5.81,5.73,5.65,5.57,5.49,5.41,5.32,5.23,5.14,5.04,4.94,4.84,4.74,4.63,4.53,4.43,4.33,4.23,4.13,4.03,3.93,3.81,3.69,3.57,3.45,3.33,3.22,3.12,3.04,2.98,2.93,2.92,2.92,2.92,2.92,2.92,2.92,2.92,2.92,2.92,2.92,2.92,2.92,2.92,2.9,2.86,2.8,2.71,2.62,2.52,2.42,2.33,2.24,2.18,2.14,2.12,2.12,2.12,2.12,2.12,2.12,2.12,2.12,2.12,2.12,2.12,2.12,2.12,2.1,2.06,2,1.91,1.82,1.71,1.61,1.5,1.4,1.32,1.25,1.2,1.16,1.13,1.1,1.06,1.03,1,0.97,0.93,0.9,0.87,0.85,0.82,0.79,0.77,0.74,0.72,0.69,0.67,0.65,0.63,0.61,0.59,0.58,0.56,0.54,0.53,0.52,0.51,0.5,0.49,0.48,0.48,0.47,0.47,0.46,0.46,0.47,0.48,0.5,0.53,0.56,0.59,0.62,0.64,0.67,0.69,0.7,0.71,0.71,0.71,0.71,0.7,0.7,0.7,0.69,0.69,0.69,0.68,0.68,0.67,0.67,0.67,0.66,0.66,0.65,0.65,0.65,0.65,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.64,0.65,0.65,0.65,0.66,0.66,0.67,0.68,0.69,0.69,0.7,0.71,0.73,0.74,0.75,0.76,0.78,0.8,0.81,0.83,0.85,0.87,0.89,0.92,0.94,0.97,0.99,1.02,1.05,1.08,1.11,1.15,1.18,1.32,1.66,2.21,2.97,3.94,5.11,6.5,8.1,9.9,11.92,14.15,16.6,22.3,22.8,24.48,30.38,35.74,42.4,57.14,94.04,112.9,123.4,130.4,130,119.4,120.7,116.8,118.1,119.4,124.8,143.5,204,294,319.2,328.4,365,350.8,347.6,347.6,325,331.6,319.2,308,308,308,308,296.8,300,281,278.4,270.6,271,253.6,233.5,219.2,207.8,205.9,204,189.6,178.8,173.4,160,154.4,146,145,140.5,130.4,126.2,116.8,112.9,106.5,101.6,98.51,82.67,67.3,80.05,76.12,72.3,71.02,69.78,67.3,67.3,68.54,57.6,71.02,66.06,59.12,57.14,55.16,55.16,52.19,52.19,51.2,48.56,44.16,43,45.92,49.44,44.16,36.48,35.74,35,32.36,37.22,32.36,32.36,32.36,33.68,32.36,31.7,35.74,29.72,32.36,30.38,29.72,28.4,28.4,28.4,27.28,25.6,25.04,23.92,22.3,21.8,21.8,21.8,22.8,21.8,25.6,22.8,22.8,17.8,16.04,16.04,16.04,16.04,16.04,16.04,16.04,16.04,16.04,16.04,15.02,14,14.03,14.11,14.25,14.45,14.72,15.06,15.46,15.95,16.51,17.15,17.87,18.69,19.59,20.59,21.69,22.88,24.18,25.59,27.1,28.73,30.48,32.34,34.33,36.44,38.69,41.06,43.57,46.22,49.01,51.95,55.04,58.27,61.66,65.21,68.92,72.8,88.09,104.9,105.7,110.3,111.6,110.3,106.5,105.7,103.3,100,97.02,98.8,91.07,83.98,88.09,81.36,78.74,77.43,77.43,73.5,74.81,72.63,68.58,66.4,68.54,69.78,67.3,64.82,61.1,59.12,56.15,53.18,50.32,49.44,44.16,36.5,42.4,37.96,37.22,33.68,36.48,35.74,35,35,37.22,37.22,39.44,32.6,34.54,36.48,35.74,34.34,33.68,33.02,31.04,29.72,29.72,29.72,26.16,25.6,29.72,18.3,22.3,21.3,21.8,21.8,20.3,20.8,25.04,25.04,25.6,25.6,25.04,25.6,25.04,25.6,23.92,25.04,21.3,21.8,22.3,21.8,20.8,16.1,20.3,18.3,13.22,19.3,19.3,18.3,14.4,13.86,13.36,12.9,12.48,12.1,11.75,11.43,11.15,10.9,10.67,10.48,10.31,10.16,10.04,9.93,9.85,9.78,9.73,9.69,9.67,9.65,9.65,12.08,8.67,11.7,11.38,10.65,9.84,9.32,9.07,8.85,8.66,8.49,8.35,8.22,8.1,7.98,7.86,7.74,7.61,7.47,7.31,7.14,6.96,6.78,6.58,6.39,6.19,5.99,5.78,5.58,5.39,5.2,5.01,4.83,4.67,4.51,4.37,4.24,4.12,4.02,3.95,3.89,3.85,3.84,4.41,5.77,7.39,8.75,9.32,9.18,9,8.94,8.88,8.83,8.78,8.73,8.68,8.64,8.6,8.56,8.53,8.5,8.47,8.45,8.42,8.4,8.39,8.37,8.36,8.35,8.35,8.34,8.34,8.67,9.65,9.62,9.53,9.4,9.21,8.98,8.7,8.4,8.06,7.69,7.3,6.89,6.47,6.03,5.59,5.14,4.7,4.26,3.83,3.42,3.02,2.65,2.3,1.98,1.7,1.45,1.25,1.09,0.99,0.94,0.92,0.91,0.89,0.87,0.85,0.84,0.82,0.81,0.79,0.78,0.77,0.75,0.74,0.73,0.72,0.71,0.7,0.69,0.68,0.67,0.66,0.65,0.64,0.64,0.63,0.63,0.62,0.62,0.61,0.61,0.61,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.61,0.61,0.61,0.61,0.61,0.61,0.62,0.62,0.62,0.62,0.63,0.63,0.63,0.63,0.63,0.64,0.64,0.64,0.64,0.64,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.64,0.63,0.62,0.6,0.59,0.57,0.55,0.54,0.53,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.51,0.51,0.51,0.5,0.5,0.49,0.48,0.47,0.47,0.46,0.45,0.45,0.44,0.43,0.42,0.42,0.41,0.41,0.41,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.41,0.42,0.43,0.44,0.46,0.48,0.5,0.53,0.55,0.58,0.61,0.64,0.67,0.7,0.73,0.77,0.8,0.83,0.87,0.9,0.93,0.96,0.99,1.02,1.05,1.08,1.1,1.12,1.14,1.16,1.17,1.18,1.19,1.2,1.2,1.2,1.19,1.17,1.15,1.12,1.09,1.06,1.02,0.98,0.94,0.9,0.86,0.82,0.78,0.74,0.7,0.66,0.63,0.6,0.57,0.55,0.53,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.52,0.51,0.51,0.5,0.5,0.49,0.49,0.48,0.47,0.47,0.47,0.46,0.46,0.45,0.45,0.45,0.44,0.44,0.44,0.43,0.43,0.43,0.42,0.42,0.42,0.41,0.41,0.41,0.41,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.41,0.41,0.41,0.41,0.41,0.41,0.41,0.41,0.41,0.41,0.41,0.41,0.41,0.41,0.41,0.42,0.42,0.42,0.42,0.42,0.42,0.42,0.42,0.42,0.43,0.43,0.43,0.43,0.43,0.43,0.44,0.44,0.44,0.44,0.44,0.44,0.45,0.45,0.45            ]        },        {            name: '降雨量',            type: 'line',            yAxisIndex: 1,            animation: false,            areaStyle: {},            lineStyle: {                width: 1            },            markArea: {                silent: true,                data: [                    [{                        xAxis: '2009/9/10\n7:00'                    }, {                        xAxis: '2009/9/20\n7:00'                    }]                ]            },            data: [                0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.005,0.017,0.017,0.017,0.017,0.011,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.021,0.026,0.03,0.036,0.036,0.195,0.221,0.019,0.013,0.017,0.03,0.03,0.03,0.046,0.045,0.038,0.084,0.045,0.045,0.037,0.034,0.035,0.036,0.044,0.052,0.048,0.109,0.033,0.029,0.04,0.042,0.042,0.042,0.073,0.076,0.062,0.066,0.066,0.075,0.096,0.128,0.121,0.128,0.14,0.226,0.143,0.097,0.018,0,0,0,0,0,0.018,0.047,0.054,0.054,0.054,0.036,0.185,0.009,0.038,0.061,0.077,0.091,0.126,0.69,0.182,0.349,0.231,0.146,0.128,0.167,0.1,0.075,0.071,0.071,0.117,0.01,0.002,0.002,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.005,0.026,0.038,0.038,0.038,0.076,0.086,0.109,0.213,0.276,0.288,0.297,0.642,1.799,1.236,2.138,0.921,0.497,0.685,0.828,0.41,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.018,0.024,0.024,0.024,0.024,0.006,0.003,0.046,0.046,0.046,0.046,0.043,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.204,0.303,1.028,1.328,1.524,1.41,1.362,1.292,1.191,0.529,0.501,0.944,1.81,2.899,0.859,0.126,0.087,0.047,0,0,0,0,0.011,0.028,0.028,0.028,0.028,0.017,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.099,0.159,0.297,0.309,0.309,0.614,0.818,1.436,1.195,0.553,0.542,0.955,0.898,0.466,0.386,0.556,0.388,0.221,0.192,0.192,0.187,0.166,0.18,0.302,0.158,0.009,0.009,0.009,0.009,0.009,0.007,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.004,0.032,0.032,0.032,0.032,0.082,0.149,0.204,0.247,0.262,0.49,0.51,0.533,0.746,0.847,2.393,1.188,1.114,0.475,0.043,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.017,0.017,0.021,0.042,0.079,0.111,0.126,0.122,0.133,0.846,0.102,0.077,0.067,0.056,0.005,0,0,0,0,0,0,0,0,0,0,0,0,0,0.011,0.017,0.017,0.017,0.017,0.006,0,0,0,0,0,0.01,0.03,0.054,0.067,0.07,0.25,0.251,0.494,0.065,0.054,0.054,0.064,0.084,0.077,0.101,0.132,0.248,0.069,0.117,0.115,0.087,0.326,0.036,0.009,0.009,0.009,0.009,0.009,0.004,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.02,0.039,0.04,0.04,0.04,0.229,0.079,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.023,0.069,0.082,0.082,0.082,0.503,0.774,0.038,0.012,0.012,0.012,0.016,0.02,0.028,0.051,0.06,0.064,0.19,0.15,0.164,0.139,0.13,0.085,0.031,0.023,0.022,0.007,0.005,0.005,0.001,0,0.02,0.048,0.048,0.053,0.056,0.036,0.008,0.008,0.004,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.013,0.017,0.036,0.068,0.095,0.233,0.272,0.377,0.722,1.494,3.756,0.954,0.439,0.442,0.462,0.373,0.249,0.214,0.1,0.044,0.037,0.023,0.002,0,0,0,0,0,0,0.02,0.024,0.024,0.024,0.024,0.004,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.008,0.017,0.017,0.045,0.186,0.308,0.241,0.241,0.893,4.067,4.494,5.015,3.494,2.057,1.411,0.718,0.407,0.313,0.339,1.537,1.105,0.218,0.136,0.03,0.005,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.037,0.448,1.2,1.309,1.309,1.425,1.223,0.471,0.767,0.423,0.273,0.412,0.646,0.481,0.239,0.131,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.044,0.15,0.223,0.388,0.513,0.883,2.828,4.786,5.959,4.95,6.434,6.319,3.35,2.806,4.204,1.395,1.015,1.015,0.836,0.74,0.72,0.615,0.477,0.192,0.046,0.007,0.007,0.007,0.007,0.007,0.007,0.007,0.008,0.005,0.005,0.005,0.005,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.001,0.012,0.012,0.012,0.012,0.011,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.002,0.012,0.028,0.028,0.028,0.138,0.092,0.082,0.082,0.096,0.719,0.155,0.042,0.047,0.129,0.021,0.021,0.014,0.009,0.029,0.067,0.088,0.095,0.095,0.138,0.091,0.032,0.025,0.025,0.003,0,0,0,0,0,0,0,0,0,0,0,0,0.002,0.045,0.228,0.297,0.325,0.339,0.581,1.244,0.796,0.517,0.227,0.053,0.006,0,0,0,0,0,0,0,0,0,0.003,0.005,0.005,0.005,0.005,0.081,0.129,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.014,0.041,0.041,0.041,0.041,0.027,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.009,0.017,0.017,0.017,0.017,0.355,0.174,0.009,0.009,0.012,0.136,0.208,0.208,0.208,0.215,7.359,1.858,0.458,0.053,0.053,0.047,0.045,0.045,0.059,0.136,0.188,0.206,0.21,0.588,1.517,6.02,4.688,4.42,0.624,0.326,0.359,0.553,0.899,0.94,2.95,9.415,5.752,1.092,0.096,0.035,0.026,0.018,0.015,0.011,0.011,0.011,0,0,0,0,0,0,0,0,0,0,0,0.056,0.27,0.314,0.351,0.354,0.609,0.796,1.857,0.848,0.538,0.214,0.178,0.178,0.201,0.231,0.227,0.272,0.397,0.45,1.014,2.917,1.675,0.081,0.059,0.059,0.148,0.075,0.075,0.078,0.236,0.784,0.784,0.784,0.784,0.741,0.115,0.058,0.058,0.058,0.029,0.015,0.015,0.015,0.015,0.012,0.008,0.604,0.985,1.305,2.273,2.528,2.336,2.496,2.281,1.397,1.713,3.259,1.167,0.745,0.548,1.058,0.684,0.728,0.392,0.179,0.283,0.283,0.46,0.08,0.099,0.099,0.099,0.1,0.143,0.137,0.238,0.317,0.262,0.225,0.792,0.426,0.332,0.261,0.11,0.093,0.102,0.171,0.292,0.504,0.605,1.745,2.485,1.964,0.33,0.171,0.259,0.242,0.215,0.366,0.354,0.205,0.203,0.262,0.153,0.13,0.137,0.362,0.691,0.295,0.433,0.154,0.056,0.053,0.053,0.053,0.051,0.047,0.065,0.078,0.091,0.206,0.813,0.102,0.151,0.05,0.024,0.004,0.001,0,0,0,0.021,0.021,0.021,0.021,0.021,0.013,0.013,0.013,0.013,0.013,0.013,0.013,0.013,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.008,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.018,0.021,0.021,0.021,0.021,0.003,0,0,0,0,0,0,0,0,0,0.024,0.173,0.261,0.267,0.267,0.534,1.354,1.772,0.72,0.218,0.018,0.018,0.028,0.036,0.032,0.194,0.082,0.035,0.286,0.027,0.038,0.038,0.027,0.021,0.014,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.016,0.017,0.017,0.031,0.047,0.043,0.056,0.104,0.149,0.179,0.205,0.328,0.998,0.522,1.851,3.727,3.273,2.204,1.169,1.006,1.179,0.74,0.741,1.065,0.925,0.671,0.497,0.431,0.327,0.277,0.126,0.581,0.207,0.359,2.485,0.038,0.036,0.003,0.003,0.003,0.003,0.004,0.098,0.023,0.021,0.021,0.022,0.041,0.041,0.043,0.045,0.043,0.014,0.014,0.014,0.014,0.014,0.014,0.014,0.031,0.046,0.063,0.119,0.107,0.092,0.085,0.065,0.06,0.054,0.042,0.039,0.046,0.044,0.028,0.028,0.02,0.013,0.013,0.013,0.013,0.016,0.032,0.031,0.031,0.031,0.028,0.011,0.011,0.011,0.011,0.011,0.023,0.024,0.024,0.024,0.019,0.015,0.015,0.015,0.015,0.015,0.015,0.013,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.001,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.011,0.017,0.024,0.026,0.061,0.172,0.206,0.213,0.267,0.511,0.668,0.157,0.017,0.017,0.017,0.046,0.054,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.001,0.017,0.017,0.017,0.017,0.016,0,0,0,0,0,0,0,0,0,0.01,0.017,0.017,0.017,0.017,0.012,0.017,0.017,0.017,0.017,0.012,0,0,0,0,0,0.003,0.031,0.066,0.093,0.112,0.122,0.202,0.068,0.041,0.022,0.011,0,0,0,0,0,0,0,0,0,0,0,0.002,0.005,0.012,0.021,0.021,0.019,0.033,0.03,0.026,0.026,0.034,0.095,0.024,0.024,0.024,0.023,0.019,0.018,0.018,0.018,0.011,0.03,0.045,0.044,0.044,0.044,0.022,0.009,0.024,0.033,0.033,0.033,0.024,0.009,0,0,0,0,0,0,0.003,0.017,0.017,0.017,0.017,0.014,0,0,0,0,0,0.032,0.032,0.032,0.032,0.032,0.005,0.008,0.009,0.014,0.014,0.009,0.005,0.004,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.007,0.009,0.009,0.009,0.009,0.043,0.063,0.084,0.098,0.101,0.213,0.334,0.383,0.43,0.448,0.511,0.801,0.835,1.642,1.614,1.496,1.496,1.476,1.068,0.481,0.22,0.119,0.099,0.07,0.072,0.063,0.076,0.14,0.205,0.28,0.297,0.3,0.479,0.877,1.098,1.611,1.629,1.686,1.686,1.631,1.528,1.862,1.703,1.531,2.196,0.395,0.416,0.453,0.728,0.917,0.986,1.17,2.171,3.011,2.909,3.301,1.377,0.778,0.799,0.947,1.039,0.879,0.76,1.372,1.674,1.674,1.68,1.823,1.793,1.162,0.783,0.216,0.152,0.152,0.152,0.049,0,0,0,0.117,0.127,0.127,0.127,0.127,0.127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.003,0.005,0.005,0.005,0.005,0.003,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.309,0.364,0.364,0.364,0.364,0.063,0.01,0.01,0.01,0.012,0.015,0.015,0.11,0.55,0.824,0.825,0.829,1.39,1.429,1.342,1.43,1.636,1.717,2.135,2.203,3.191,3.022,1.589,0.86,0.807,0.645,0.595,0.588,0.557,0.552,1.271,0.708,0.677,0.629,0.714,0.203,0.133,0.061,0.062,0.018,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.001,0.072,0.29,0.438,0.53,0.557,0.873,1.039,1.04,0.208,0.049,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.03,0.039,0.039,0.039,0.039,0.098,0.008,0.007,0.007,0.007,0.007,0.007,0.007,0.007,0.007,0.007,0.056,0.062,0.065,0.065,0.065,0.047,0.216,0.256,0.315,0.4,0.502,0.449,0.47,0.571,0.814,1.153,0.774,0.202,0.086,0.075,0.071,0.032,0.019,0.003,0.004,0.004,0.004,0.004,0.004,0.004,0.007,0.072,0.153,0.256,0.306,0.404,0.698,0.733,0.823,0.715,0.563,0.404,0.293,0.217,0.213,0.202,0.202,0.294,0.704,0.797,1.359,1.101,0.72,0.514,0.539,0.434,0.389,0.387,0.386,0.375,0.369,0.319,0.239,0.183,0.136,0.062,0.052,0.096,0.119,0.119,0.114,0.127,0.132,0.139,0.169,0.191,0.278,0.254,0.214,0.237,0.221,0.143,0.129,0.125,0.109,0.1,0.087,0.06,0.038,0.029,0.029,0.028,0.048,0.053,0.053,0.111,0.125,0.102,0.097,0.097,0.039,0.02,0.02,0.02,0.014,0.004,0.031,0.043,0.047,0.052,0.08,0.144,0.182,0.176,0.171,0.149,0.112,0.025,0,0,0,0,0,0,0,0.016,0.031,0.031,0.031,0.031,0.015,0,0,0,0,0,0.005,0.005,0.005,0.005,0.005,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.005,0.005,0.005,0.005,0.005,0.001,0,0,0            ]        }    ]};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script><ul><li><a href="https://echarts.apache.org/examples/zh/editor.html?c=line-easing">Line Easing Visualizing</a></li></ul><script>var easingFuncs = {    linear: function (k) {        return k;    },    quadraticIn: function (k) {        return k * k;    },    quadraticOut: function (k) {        return k * (2 - k);    },    quadraticInOut: function (k) {        if ((k *= 2) < 1) { return 0.5 * k * k; }        return -0.5 * (--k * (k - 2) - 1);    },    cubicIn: function (k) {        return k * k * k;    },    cubicOut: function (k) {        return --k * k * k + 1;    },    cubicInOut: function (k) {        if ((k *= 2) < 1) { return 0.5 * k * k * k; }        return 0.5 * ((k -= 2) * k * k + 2);    },    quarticIn: function (k) {        return k * k * k * k;    },    quarticOut: function (k) {        return 1 - (--k * k * k * k);    },    quarticInOut: function (k) {        if ((k *= 2) < 1) { return 0.5 * k * k * k * k; }        return -0.5 * ((k -= 2) * k * k * k - 2);    },    quinticIn: function (k) {        return k * k * k * k * k;    },    quinticOut: function (k) {        return --k * k * k * k * k + 1;    },    quinticInOut: function (k) {        if ((k *= 2) < 1) { return 0.5 * k * k * k * k * k; }        return 0.5 * ((k -= 2) * k * k * k * k + 2);    },    sinusoidalIn: function (k) {        return 1 - Math.cos(k * Math.PI / 2);    },    sinusoidalOut: function (k) {        return Math.sin(k * Math.PI / 2);    },    sinusoidalInOut: function (k) {        return 0.5 * (1 - Math.cos(Math.PI * k));    },    exponentialIn: function (k) {        return k === 0 ? 0 : Math.pow(1024, k - 1);    },    exponentialOut: function (k) {        return k === 1 ? 1 : 1 - Math.pow(2, -10 * k);    },    exponentialInOut: function (k) {        if (k === 0) {            return 0;        }        if (k === 1) {            return 1;        }        if ((k *= 2) < 1) {            return 0.5 * Math.pow(1024, k - 1);        }        return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2);    },    circularIn: function (k) {        return 1 - Math.sqrt(1 - k * k);    },    circularOut: function (k) {        return Math.sqrt(1 - (--k * k));    },    circularInOut: function (k) {        if ((k *= 2) < 1) { return -0.5 * (Math.sqrt(1 - k * k) - 1); }        return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);    },    elasticIn: function (k) {        var s;        var a = 0.1;        var p = 0.4;        if (k === 0) { return 0; }        if (k === 1) { return 1; }        if (!a || a < 1) { a = 1; s = p / 4; }        else { s = p * Math.asin(1 / a) / (2 * Math.PI); }        return -(a * Math.pow(2, 10 * (k -= 1)) * Math.sin((k - s) * (2 * Math.PI) / p));    },    elasticOut: function (k) {        var s;        var a = 0.1;        var p = 0.4;        if (k === 0) { return 0; }        if (k === 1) { return 1; }        if (!a || a < 1) { a = 1; s = p / 4; }        else { s = p * Math.asin(1 / a) / (2 * Math.PI); }        return (a * Math.pow(2, -10 * k) * Math.sin((k - s) * (2 * Math.PI) / p) + 1);    },    elasticInOut: function (k) {        var s;        var a = 0.1;        var p = 0.4;        if (k === 0) { return 0; }        if (k === 1) { return 1; }        if (!a || a < 1) { a = 1; s = p / 4; }        else { s = p * Math.asin(1 / a) / (2 * Math.PI); }        if ((k *= 2) < 1) {            return -0.5 * (a * Math.pow(2, 10 * (k -= 1)) * Math.sin((k - s) * (2 * Math.PI) / p));        }        return a * Math.pow(2, -10 * (k -= 1)) * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1;    },    backIn: function (k) {        var s = 1.70158;        return k * k * ((s + 1) * k - s);    },    backOut: function (k) {        var s = 1.70158;        return --k * k * ((s + 1) * k + s) + 1;    },    backInOut: function (k) {        var s = 1.70158 * 1.525;        if ((k *= 2) < 1) { return 0.5 * (k * k * ((s + 1) * k - s)); }        return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);    },    bounceIn: function (k) {        return 1 - easingFuncs.bounceOut(1 - k);    },    bounceOut: function (k) {        if (k < (1 / 2.75)) { return 7.5625 * k * k; }        else if (k < (2 / 2.75)) { return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75; }        else if (k < (2.5 / 2.75)) { return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375; }        else { return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375; }    },    bounceInOut: function (k) {        if (k < 0.5) { return easingFuncs.bounceIn(k * 2) * 0.5; }        return easingFuncs.bounceOut(k * 2 - 1) * 0.5 + 0.5;    }};var N_POINT = 30;var grids = [];var xAxes = [];var yAxes = [];var series = [];var titles = [];var count = 0;echarts.util.each(easingFuncs, function (easingFunc, name) {    var data = [];    for (var i = 0; i <= N_POINT; i++) {        var x = i / N_POINT;        var y = easingFunc(x);        data.push([x, y]);    }    grids.push({        show: true,        borderWidth: 0,        backgroundColor: '#fff',        shadowColor: 'rgba(0, 0, 0, 0.3)',        shadowBlur: 2    });    xAxes.push({        type: 'value',        show: false,        min: 0,        max: 1,        gridIndex: count    });    yAxes.push({        type: 'value',        show: false,        min: -0.4,        max: 1.4,        gridIndex: count    });    series.push({        name: name,        type: 'line',        xAxisIndex: count,        yAxisIndex: count,        data: data,        showSymbol: false,        animationEasing: name,        animationDuration: 1000    });    titles.push({        textAlign: 'center',        text: name,        textStyle: {            fontSize: 12,            fontWeight: 'normal'        }    });    count++;});var rowNumber = Math.ceil(Math.sqrt(count));echarts.util.each(grids, function (grid, idx) {    grid.left = ((idx % rowNumber) / rowNumber * 100 + 0.5) + '%';    grid.top = (Math.floor(idx / rowNumber) / rowNumber * 100 + 0.5) + '%';    grid.width = (1 / rowNumber * 100 - 1) + '%';    grid.height = (1 / rowNumber * 100 - 1) + '%';    titles[idx].left = parseFloat(grid.left) + parseFloat(grid.width) / 2 + '%';    titles[idx].top = parseFloat(grid.top) + '%';});</script><div id="echarts4868" style="width: 85%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts4868'));        // 指定图表的配置项和数据        var option = option = {    title: titles.concat([{        text: 'Different Easing Functions',        top: 'bottom',        left: 'center'    }]),    grid: grids,    xAxis: xAxes,    yAxis: yAxes,    series: series};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script><h3 id="柱状图Bar"><a href="#柱状图Bar" class="headerlink" title="柱状图Bar"></a>柱状图Bar</h3><ul><li><a href="https://echarts.apache.org/examples/zh/editor.html?c=bar-animation-delay">柱状图动画延迟</a></li></ul><script>var xAxisData = [];var data01 = [];var data02 = [];for (var i = 0; i < 100; i++) {    xAxisData.push('类目' + i);    data01.push((Math.sin(i / 5) * (i / 5 -10) + i / 6) * 5);    data02.push((Math.cos(i / 5) * (i / 5 -10) + i / 6) * 5);}</script><div id="echarts6190" style="width: 85%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts6190'));        // 指定图表的配置项和数据        var option = option = {    title: {        text: '柱状图动画延迟'    },    legend: {        data: ['bar01', 'bar02']    },    toolbox: {        // y: 'bottom',        feature: {            magicType: {                type: ['stack', 'tiled']            },            dataView: {},            saveAsImage: {                pixelRatio: 2            }        }    },    tooltip: {},    xAxis: {        data: xAxisData,        splitLine: {            show: false        }    },    yAxis: {    },    series: [{        name: 'bar01',        type: 'bar',        data: data01,        animationDelay: function (idx) {            return idx * 10 + 50;        }    }, {        name: 'bar02',        type: 'bar',        data: data02,        animationDelay: function (idx) {            return idx * 10 + 100;        }    }],    animationEasing: 'elasticOut',    animationDelayUpdate: function (idx) {        return idx * 5;    }};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script><ul><li><a href="https://echarts.apache.org/examples/zh/editor.html?c=bar-brush">柱状图框选</a></li></ul><p><script><br>var xAxisData = [];<br>var data1 = [];<br>var data2 = [];<br>var data3 = [];<br>var data4 = [];</p><p>for (var i = 0; i &lt; 10; i++) {<br>    xAxisData.push(‘Class’ + i);<br>    data1.push((Math.random() <em> 2).toFixed(2));<br>    data2.push(-Math.random().toFixed(2));<br>    data3.push((Math.random() </em> 5).toFixed(2));<br>    data4.push((Math.random() + 0.3).toFixed(2));<br>}</p><p>var emphasisStyle = {<br>    itemStyle: {<br>        barBorderWidth: 1,<br>        shadowBlur: 10,<br>        shadowOffsetX: 0,<br>        shadowOffsetY: 0,<br>        shadowColor: ‘rgba(0,0,0,0.5)’<br>    }<br>};</p><p>myChart.on(‘brushSelected’, renderBrushed);</p><p>function renderBrushed(params) {<br>    var brushed = [];<br>    var brushComponent = params.batch[0];</p><pre><code class="hljs">for (var sIdx = 0; sIdx &lt; brushComponent.selected.length; sIdx++) &#123;    var rawIndices = brushComponent.selected[sIdx].dataIndex;    brushed.push(&#39;[Series &#39; + sIdx + &#39;] &#39; + rawIndices.join(&#39;, &#39;));&#125;myChart.setOption(&#123;    title: &#123;        backgroundColor: &#39;#333&#39;,        text: &#39;SELECTED DATA INDICES: \n&#39; + brushed.join(&#39;\n&#39;),        bottom: 0,        right: 0,        width: 100,        textStyle: &#123;            fontSize: 12,            color: &#39;#fff&#39;        &#125;    &#125;&#125;);</code></pre><p>}<br>&lt;/script&gt;<br><div id="echarts507" style="width: 85%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts507'));        // 指定图表的配置项和数据        var option = option = {    backgroundColor: '#eee',    legend: {        data: ['bar', 'bar2', 'bar3', 'bar4'],        left: 10    },    brush: {        toolbox: ['rect', 'polygon', 'lineX', 'lineY', 'keep', 'clear'],        xAxisIndex: 0    },    toolbox: {        feature: {            magicType: {                type: ['stack', 'tiled']            },            dataView: {}        }    },    tooltip: {},    xAxis: {        data: xAxisData,        name: 'X Axis',        axisLine: {onZero: true},        splitLine: {show: false},        splitArea: {show: false}    },    yAxis: {        inverse: true,        splitArea: {show: false}    },    grid: {        left: 100    },    visualMap: {        type: 'continuous',        dimension: 1,        text: ['High', 'Low'],        inverse: true,        itemHeight: 200,        calculable: true,        min: -2,        max: 6,        top: 60,        left: 10,        inRange: {            colorLightness: [0.4, 0.8]        },        outOfRange: {            color: '#bbb'        },        controller: {            inRange: {                color: '#2f4554'            }        }    },    series: [        {            name: 'bar',            type: 'bar',            stack: 'one',            emphasis: emphasisStyle,            data: data1        },        {            name: 'bar2',            type: 'bar',            stack: 'one',            emphasis: emphasisStyle,            data: data2        },        {            name: 'bar3',            type: 'bar',            stack: 'two',            emphasis: emphasisStyle,            data: data3        },        {            name: 'bar4',            type: 'bar',            stack: 'two',            emphasis: emphasisStyle,            data: data4        }    ]};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script></p><ul><li><a href="https://echarts.apache.org/examples/zh/editor.html?c=bar-polar-stack-radial">极坐标系下的堆叠柱状图</a></li></ul><script><p>&lt;/script&gt;<br><div id="echarts1880" style="width: 85%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts1880'));        // 指定图表的配置项和数据        var option = option = {    angleAxis: {        type: 'category',        data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']    },    radiusAxis: {    },    polar: {    },    series: [{        type: 'bar',        data: [1, 2, 3, 4, 3, 5, 1],        coordinateSystem: 'polar',        name: 'A',        stack: 'a'    }, {        type: 'bar',        data: [2, 4, 6, 1, 3, 2, 1],        coordinateSystem: 'polar',        name: 'B',        stack: 'a'    }, {        type: 'bar',        data: [1, 2, 3, 4, 1, 2, 5],        coordinateSystem: 'polar',        name: 'C',        stack: 'a'    }],    legend: {        show: true,        data: ['A', 'B', 'C']    }};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script></p><h3 id="饼图Pie"><a href="#饼图Pie" class="headerlink" title="饼图Pie"></a>饼图Pie</h3><ul><li><a href="https://echarts.apache.org/examples/zh/editor.html?c=pie-pattern">Texture on Pie Chart</a></li></ul><p><script><br>var piePatternSrc = ‘data:image/jpeg;base64,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’;<br>var bgPatternSrc = ‘data:image/png;charset=utf-8;base64,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’;</p><p>var piePatternImg = new Image();<br>piePatternImg.src = piePatternSrc;<br>var bgPatternImg = new Image();<br>bgPatternImg.src = bgPatternSrc;</p><p>var itemStyle = {<br>    normal: {<br>        opacity: 0.7,<br>        color: {<br>            image: piePatternImg,<br>            repeat: ‘repeat’<br>        },<br>        borderWidth: 3,<br>        borderColor: ‘#235894’<br>    }<br>};<br>&lt;/script&gt;<br><div id="echarts3684" style="width: 85%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts3684'));        // 指定图表的配置项和数据        var option = option = {    backgroundColor: {        image: bgPatternImg,        repeat: 'repeat'    },    title: {        text: '饼图纹理',        textStyle: {            color: '#235894'        }    },    tooltip: {},    series: [{        name: 'pie',        type: 'pie',        selectedMode: 'single',        selectedOffset: 30,        clockwise: true,        label: {            fontSize: 18,            color: '#235894'        },        labelLine: {            lineStyle: {                color: '#235894'            }        },        data: [            {value: 335, name: '直接访问'},            {value: 310, name: '邮件营销'},            {value: 234, name: '联盟广告'},            {value: 135, name: '视频广告'},            {value: 1548, name: '搜索引擎'}        ],        itemStyle: itemStyle    }]};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script></p><h3 id="地理坐标-地图GEO-Map"><a href="#地理坐标-地图GEO-Map" class="headerlink" title="地理坐标/地图GEO/Map"></a>地理坐标/地图GEO/Map</h3><ul><li><a href="https://echarts.apache.org/examples/zh/editor.html?c=effectScatter-bmap">Air Quality - Baidu Map</a></li></ul><iframe src="https://pxxyyz.com/html-file/echarts/lines-bmap-bus.html" width="100%" height="600" name="topFrame" scrolling="yes" noresize="noresize" frameborder="0" id="topFrame"></iframe><p>可进入<a href="https://pxxyyz.com/html-file/echarts/lines-bmap-bus.html">页面查看</a></p><h3 id="K-线图Candlestick"><a href="#K-线图Candlestick" class="headerlink" title="K 线图Candlestick"></a>K 线图Candlestick</h3><ul><li><a href="https://echarts.apache.org/examples/zh/editor.html?c=candlestick-brush">Candlestick Brush</a></li></ul><iframe src="https://pxxyyz.com/html-file/echarts/candlestick-brush.html" width="100%" height="600" name="topFrame" scrolling="yes" noresize="noresize" frameborder="0" id="topFrame"></iframe><p>可进入<a href="https://pxxyyz.com/html-file/echarts/candlestick-brush.html">页面查看</a></p><h3 id="雷达图Radar"><a href="#雷达图Radar" class="headerlink" title="雷达图Radar"></a>雷达图Radar</h3><ul><li><a href="https://echarts.apache.org/examples/zh/editor.html?c=radar2">Proportion of Browsers</a></li></ul><div id="echarts3167" style="width: 85%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts3167'));        // 指定图表的配置项和数据        var option = option = {    title: {        text: '浏览器占比变化',        subtext: '纯属虚构',        top: 10,        left: 10    },    tooltip: {        trigger: 'item',        backgroundColor: 'rgba(0,0,250,0.2)'    },    legend: {        type: 'scroll',        bottom: 10,        data: (function (){            var list = [];            for (var i = 1; i <=28; i++) {                list.push(i + 2000 + '');            }            return list;        })()    },    visualMap: {        top: 'middle',        right: 10,        color: ['red', 'yellow'],        calculable: true    },    radar: {        indicator: [            { text: 'IE8-', max: 400},            { text: 'IE9+', max: 400},            { text: 'Safari', max: 400},            { text: 'Firefox', max: 400},            { text: 'Chrome', max: 400}        ]    },    series: (function (){        var series = [];        for (var i = 1; i <= 28; i++) {            series.push({                name: '浏览器（数据纯属虚构）',                type: 'radar',                symbol: 'none',                lineStyle: {                    width: 1                },                emphasis: {                    areaStyle: {                        color: 'rgba(0,250,0,0.3)'                    }                },                data: [{                    value: [                        (40 - i) * 10,                        (38 - i) * 4 + 60,                        i * 5 + 10,                        i * 9,                        i * i /2                    ],                    name: i + 2000 + ''                }]            });        }        return series;    })()};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script><h3 id="关系图Graph"><a href="#关系图Graph" class="headerlink" title="关系图Graph"></a>关系图Graph</h3><ul><li><a href="https://echarts.apache.org/examples/zh/editor.html?c=graph-webkit-dep">Graph Webkit Dep</a></li></ul><iframe src="https://pxxyyz.com/html-file/echarts/graph-webkit-dep.html" width="100%" height="600" name="topFrame" scrolling="yes" noresize="noresize" frameborder="0" id="topFrame"></iframe><p>可进入<a href="https://pxxyyz.com/html-file/echarts/graph-webkit-dep.html">页面查看</a></p><h3 id="树图Tree"><a href="#树图Tree" class="headerlink" title="树图Tree"></a>树图Tree</h3><ul><li><a href="https://echarts.apache.org/examples/zh/editor.html?c=tree-legend">Multiple Trees</a></li></ul><p><script><br>var data = {<br>    “name”: “flare”,<br>    “children”: [<br>        {<br>            “name”: “data”,<br>            “children”: [<br>                {<br>                     “name”: “converters”,<br>                     “children”: [<br>                      {“name”: “Converters”, “value”: 721},<br>                      {“name”: “DelimitedTextConverter”, “value”: 4294}<br>                     ]<br>                },<br>                {<br>                    “name”: “DataUtil”,<br>                    “value”: 3322<br>                }<br>            ]<br>        },<br>        {<br>            “name”: “display”,<br>            “children”: [<br>                {“name”: “DirtySprite”, “value”: 8833},<br>                {“name”: “LineSprite”, “value”: 1732},<br>                {“name”: “RectSprite”, “value”: 3623}<br>           ]<br>        },<br>        {<br>            “name”: “flex”,<br>            “children”: [<br>                {“name”: “FlareVis”, “value”: 4116}<br>            ]<br>        },<br>        {<br>           “name”: “query”,<br>           “children”: [<br>            {“name”: “AggregateExpression”, “value”: 1616},<br>            {“name”: “And”, “value”: 1027},<br>            {“name”: “Arithmetic”, “value”: 3891},<br>            {“name”: “Average”, “value”: 891},<br>            {“name”: “BinaryExpression”, “value”: 2893},<br>            {“name”: “Comparison”, “value”: 5103},<br>            {“name”: “CompositeExpression”, “value”: 3677},<br>            {“name”: “Count”, “value”: 781},<br>            {“name”: “DateUtil”, “value”: 4141},<br>            {“name”: “Distinct”, “value”: 933},<br>            {“name”: “Expression”, “value”: 5130},<br>            {“name”: “ExpressionIterator”, “value”: 3617},<br>            {“name”: “Fn”, “value”: 3240},<br>            {“name”: “If”, “value”: 2732},<br>            {“name”: “IsA”, “value”: 2039},<br>            {“name”: “Literal”, “value”: 1214},<br>            {“name”: “Match”, “value”: 3748},<br>            {“name”: “Maximum”, “value”: 843},<br>            {<br>             “name”: “methods”,<br>             “children”: [<br>              {“name”: “add”, “value”: 593},<br>              {“name”: “and”, “value”: 330},<br>              {“name”: “average”, “value”: 287},<br>              {“name”: “count”, “value”: 277},<br>              {“name”: “distinct”, “value”: 292},<br>              {“name”: “div”, “value”: 595},<br>              {“name”: “eq”, “value”: 594},<br>              {“name”: “fn”, “value”: 460},<br>              {“name”: “gt”, “value”: 603},<br>              {“name”: “gte”, “value”: 625},<br>              {“name”: “iff”, “value”: 748},<br>              {“name”: “isa”, “value”: 461},<br>              {“name”: “lt”, “value”: 597},<br>              {“name”: “lte”, “value”: 619},<br>              {“name”: “max”, “value”: 283},<br>              {“name”: “min”, “value”: 283},<br>              {“name”: “mod”, “value”: 591},<br>              {“name”: “mul”, “value”: 603},<br>              {“name”: “neq”, “value”: 599},<br>              {“name”: “not”, “value”: 386},<br>              {“name”: “or”, “value”: 323},<br>              {“name”: “orderby”, “value”: 307},<br>              {“name”: “range”, “value”: 772},<br>              {“name”: “select”, “value”: 296},<br>              {“name”: “stddev”, “value”: 363},<br>              {“name”: “sub”, “value”: 600},<br>              {“name”: “sum”, “value”: 280},<br>              {“name”: “update”, “value”: 307},<br>              {“name”: “variance”, “value”: 335},<br>              {“name”: “where”, “value”: 299},<br>              {“name”: “xor”, “value”: 354},<br>              {“name”: “_”, “value”: 264}<br>             ]<br>            },<br>            {“name”: “Minimum”, “value”: 843},<br>            {“name”: “Not”, “value”: 1554},<br>            {“name”: “Or”, “value”: 970},<br>            {“name”: “Query”, “value”: 13896},<br>            {“name”: “Range”, “value”: 1594},<br>            {“name”: “StringUtil”, “value”: 4130},<br>            {“name”: “Sum”, “value”: 791},<br>            {“name”: “Variable”, “value”: 1124},<br>            {“name”: “Variance”, “value”: 1876},<br>            {“name”: “Xor”, “value”: 1101}<br>           ]<br>          },<br>        {<br>           “name”: “scale”,<br>           “children”: [<br>            {“name”: “IScaleMap”, “value”: 2105},<br>            {“name”: “LinearScale”, “value”: 1316},<br>            {“name”: “LogScale”, “value”: 3151},<br>            {“name”: “OrdinalScale”, “value”: 3770},<br>            {“name”: “QuantileScale”, “value”: 2435},<br>            {“name”: “QuantitativeScale”, “value”: 4839},<br>            {“name”: “RootScale”, “value”: 1756},<br>            {“name”: “Scale”, “value”: 4268},<br>            {“name”: “ScaleType”, “value”: 1821},<br>            {“name”: “TimeScale”, “value”: 5833}<br>           ]<br>        }<br>    ]<br>};</p><p>var data2 = {<br>    “name”: “flare”,<br>    “children”: [<br>        {<br>            “name”: “flex”,<br>            “children”: [<br>                {“name”: “FlareVis”, “value”: 4116}<br>            ]<br>        },<br>        {<br>            “name”: “scale”,<br>            “children”: [<br>                {“name”: “IScaleMap”, “value”: 2105},<br>                {“name”: “LinearScale”, “value”: 1316},<br>                {“name”: “LogScale”, “value”: 3151},<br>                {“name”: “OrdinalScale”, “value”: 3770},<br>                {“name”: “QuantileScale”, “value”: 2435},<br>                {“name”: “QuantitativeScale”, “value”: 4839},<br>                {“name”: “RootScale”, “value”: 1756},<br>                {“name”: “Scale”, “value”: 4268},<br>                {“name”: “ScaleType”, “value”: 1821},<br>                {“name”: “TimeScale”, “value”: 5833}<br>           ]<br>        },<br>        {<br>            “name”: “display”,<br>            “children”: [<br>                {“name”: “DirtySprite”, “value”: 8833}<br>           ]<br>        }<br>    ]<br>};<br>&lt;/script&gt;<br><div id="echarts7872" style="width: 85%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts7872'));        // 指定图表的配置项和数据        var option = option = {    tooltip: {        trigger: 'item',        triggerOn: 'mousemove'    },    legend: {        top: '2%',        left: '3%',        orient: 'vertical',        data: [{            name: 'tree1',            icon: 'rectangle'        },        {            name: 'tree2',            icon: 'rectangle'        }],        borderColor: '#c23531'    },    series:[        {            type: 'tree',            name: 'tree1',                data: [data],                top: '5%',            left: '7%',            bottom: '2%',            right: '60%',                symbolSize: 7,                label: {                position: 'left',                verticalAlign: 'middle',                align: 'right'            },                leaves: {                label: {                    position: 'right',                    verticalAlign: 'middle',                    align: 'left'                }            },                expandAndCollapse: true,                animationDuration: 550,            animationDurationUpdate: 750            },        {            type: 'tree',            name: 'tree2',            data: [data2],                top: '20%',            left: '60%',            bottom: '22%',            right: '18%',                symbolSize: 7,                label: {                position: 'left',                verticalAlign: 'middle',                align: 'right'            },                leaves: {                label: {                    position: 'right',                    verticalAlign: 'middle',                    align: 'left'                }            },                expandAndCollapse: true,                animationDuration: 550,            animationDurationUpdate: 750        }    ]}        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script></p><h3 id="日历坐标系Calendar"><a href="#日历坐标系Calendar" class="headerlink" title="日历坐标系Calendar"></a>日历坐标系Calendar</h3><ul><li><a href="https://echarts.apache.org/examples/zh/editor.html?c=calendar-horizontal">Calendar Heatmap Horizontal</a></li></ul><p><script>function getVirtulData(year) {    year = year || '2017';    var date = +echarts.number.parseDate(year + '-01-01');    var end = +echarts.number.parseDate((+year + 1) + '-01-01');    var dayTime = 3600 * 24 * 1000;    var data = [];    for (var time = date; time < end; time += dayTime) {        data.push([            echarts.format.formatTime('yyyy-MM-dd', time),            Math.floor(Math.random() * 1000)        ]);    }    return data;}</script><br><div id="echarts6081" style="width: 85%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts6081'));        // 指定图表的配置项和数据        var option = option = {    tooltip: {        position: 'top'    },    visualMap: {        min: 0,        max: 1000,        calculable: true,        orient: 'horizontal',        left: 'center',        top: 'top'    },    calendar: [{        range: '2020',        cellSize: ['auto', 15]    },    {        top: 260,        range: '2019',        cellSize: ['auto', 15]    }],        series: [{        type: 'heatmap',        coordinateSystem: 'calendar',        calendarIndex: 0,        data: getVirtulData(2020)    }, {        type: 'heatmap',        coordinateSystem: 'calendar',        calendarIndex: 1,        data: getVirtulData(2019)    }]};        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script></p><h3 id="3D"><a href="#3D" class="headerlink" title="3D"></a>3D</h3><ul><li><a href="https://echarts.apache.org/examples/zh/editor.html?c=bar3d-simplex-noise&amp;gl=1&amp;theme=dark">Bar3D - Simplex Noise</a></li></ul><iframe src="https://pxxyyz.com/html-file/echarts/bar-3D-simplex-noise.html" width="100%" height="600" name="topFrame" scrolling="yes" noresize="noresize" frameborder="0" id="topFrame"></iframe><p>可进入<a href="https://pxxyyz.com/html-file/echarts/bar-3D-simplex-noise.html">页面查看</a></p><ul><li><a href="https://echarts.apache.org/examples/zh/editor.html?c=surface-wave&amp;gl=1">Surface Wave</a></li></ul><div id="echarts3359" style="width: 85%;height: 400px;margin: 0 auto"></div><script type="text/javascript">        // 基于准备好的dom，初始化echarts实例        var myChart = echarts.init(document.getElementById('echarts3359'));        // 指定图表的配置项和数据        var option = option = {    tooltip: {},    backgroundColor: '#fff',    visualMap: {        show: false,        dimension: 2,        min: -1,        max: 1,        inRange: {            color: ['#313695', '#4575b4', '#74add1', '#abd9e9', '#e0f3f8', '#ffffbf', '#fee090', '#fdae61', '#f46d43', '#d73027', '#a50026']        }    },    xAxis3D: {        type: 'value'    },    yAxis3D: {        type: 'value'    },    zAxis3D: {        type: 'value',        max: 1,        splitNumber: 2    },    grid3D: {        viewControl: {            // projection: 'orthographic'        },        boxHeight: 40    },    series: [{        type: 'surface',        wireframe: {            show: false        },        shading: 'color',        equation: {            x: {                step: 0.05,                min: -3,                max: 3,            },            y: {                step: 0.05,                min: -3,                max: 3,            },            z: function (x, y) {                return Math.sin(x * x + y * y) * x / 3.14            }        }    }]}        // 使用刚指定的配置项和数据显示图表。        myChart.setOption(option);</script><h2 id="pyecharts"><a href="#pyecharts" class="headerlink" title="pyecharts"></a>pyecharts</h2><script type="math/tex; mode=display">\text{py} + \text{echarts} = \text{pyecharts}</script><h3 id="是什么"><a href="#是什么" class="headerlink" title="是什么"></a>是什么</h3><blockquote><p>Echarts 是一个由百度开源的数据可视化，凭借着良好的交互性，精巧的图表设计，得到了众多开发者的认可。而 Python 是一门富有表达力的语言，很适合用于数据处理。当数据分析遇上数据可视化时，pyecharts 诞生了<sup id="fnref:2" class="footnote-ref"><a href="#fn:2" rel="footnote"><span class="hint--top hint--rounded" aria-label="[pyecharts仓库](https://github.com/pyecharts/pyecharts)">[2]</span></a></sup>。</p></blockquote><h3 id="怎么用—py或jupyter"><a href="#怎么用—py或jupyter" class="headerlink" title="怎么用—py或jupyter"></a>怎么用—py或jupyter</h3><ul><li>pip 安装</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ pip install pyecharts -U<br></code></pre></td></tr></table></figure><ul><li>生成 HTML</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> pyecharts.charts <span class="hljs-keyword">import</span> Bar<br><span class="hljs-keyword">from</span> pyecharts <span class="hljs-keyword">import</span> options <span class="hljs-keyword">as</span> opts<br><br><span class="hljs-comment"># V1 版本开始支持链式调用</span><br>bar = (<br>    Bar()<br>    .add_xaxis([<span class="hljs-string">&quot;衬衫&quot;</span>, <span class="hljs-string">&quot;毛衣&quot;</span>, <span class="hljs-string">&quot;领带&quot;</span>, <span class="hljs-string">&quot;裤子&quot;</span>, <span class="hljs-string">&quot;风衣&quot;</span>, <span class="hljs-string">&quot;高跟鞋&quot;</span>, <span class="hljs-string">&quot;袜子&quot;</span>])<br>    .add_yaxis(<span class="hljs-string">&quot;商家A&quot;</span>, [<span class="hljs-number">114</span>, <span class="hljs-number">55</span>, <span class="hljs-number">27</span>, <span class="hljs-number">101</span>, <span class="hljs-number">125</span>, <span class="hljs-number">27</span>, <span class="hljs-number">105</span>])<br>    .add_yaxis(<span class="hljs-string">&quot;商家B&quot;</span>, [<span class="hljs-number">57</span>, <span class="hljs-number">134</span>, <span class="hljs-number">137</span>, <span class="hljs-number">129</span>, <span class="hljs-number">145</span>, <span class="hljs-number">60</span>, <span class="hljs-number">49</span>])<br>    .set_global_opts(title_opts=opts.TitleOpts(title=<span class="hljs-string">&quot;某商场销售情况&quot;</span>))<br>)<br>bar.render()<br><br><span class="hljs-comment"># 不习惯链式调用的开发者依旧可以单独调用方法</span><br>bar = Bar()<br>bar.add_xaxis([<span class="hljs-string">&quot;衬衫&quot;</span>, <span class="hljs-string">&quot;毛衣&quot;</span>, <span class="hljs-string">&quot;领带&quot;</span>, <span class="hljs-string">&quot;裤子&quot;</span>, <span class="hljs-string">&quot;风衣&quot;</span>, <span class="hljs-string">&quot;高跟鞋&quot;</span>, <span class="hljs-string">&quot;袜子&quot;</span>])<br>bar.add_yaxis(<span class="hljs-string">&quot;商家A&quot;</span>, [<span class="hljs-number">114</span>, <span class="hljs-number">55</span>, <span class="hljs-number">27</span>, <span class="hljs-number">101</span>, <span class="hljs-number">125</span>, <span class="hljs-number">27</span>, <span class="hljs-number">105</span>])<br>bar.add_yaxis(<span class="hljs-string">&quot;商家B&quot;</span>, [<span class="hljs-number">57</span>, <span class="hljs-number">134</span>, <span class="hljs-number">137</span>, <span class="hljs-number">129</span>, <span class="hljs-number">145</span>, <span class="hljs-number">60</span>, <span class="hljs-number">49</span>])<br>bar.set_global_opts(title_opts=opts.TitleOpts(title=<span class="hljs-string">&quot;某商场销售情况&quot;</span>))<br>bar.render()<br></code></pre></td></tr></table></figure><ul><li><strong>Demo</strong>：pyecharts 画廊<sup id="fnref:3" class="footnote-ref"><a href="#fn:3" rel="footnote"><span class="hint--top hint--rounded" aria-label="[pyecharts 画廊](https://github.com/pyecharts/pyecharts-gallery)">[3]</span></a></sup></li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1" class="footnote-text"><span><a href="https://echarts.apache.org/zh/index.html">echarts 官网</a><a href="#fnref:1" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:2" class="footnote-text"><span><a href="https://github.com/pyecharts/pyecharts">pyecharts仓库</a><a href="#fnref:2" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:3" class="footnote-text"><span><a href="https://github.com/pyecharts/pyecharts-gallery">pyecharts 画廊</a><a href="#fnref:3" rev="footnote" class="footnote-backref"> ↩</a></span></span></li></ol></div></section>]]></content>
    
    
    <categories>
      
      <category>功能增强</category>
      
    </categories>
    
    
    <tags>
      
      <tag>Hexo</tag>
      
      <tag>用户经验</tag>
      
      <tag>花里胡哨</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>给博客文章嵌入 PPT 演示</title>
    <link href="/blog/posts/9604e2c4.html"/>
    <url>/blog/posts/9604e2c4.html</url>
    
    <content type="html"><![CDATA[<div class="note note-success">            <p>本文由 Fluid 用户授权转载，版权归原作者所有。</p> <p>本文作者：pxxyyz</p> <p>原文地址：<a href="https://pxxyyz.com/posts/44941/">https://pxxyyz.com/posts/44941/</a></p>           </div><h2 id="效果"><a href="#效果" class="headerlink" title="效果"></a>效果</h2><iframe src="https://pxxyyz.com/nodeppt/%E5%A4%9A%E5%A4%8D%E5%8F%98%E8%BF%91%E6%9C%9F%E8%BF%9B%E5%B1%95/demo.html" width="100%" height="600" name="topFrame" scrolling="yes" noresize="noresize" frameborder="0" id="topFrame"></iframe><div class="note note-light">            <p><strong>可以通过鼠标和键盘控制</strong></p> <ul> <li>页面: ↑/↓/←/→ Space Home End（空格,home键,end键）</li> <li>全屏: F</li> <li>Overview: -/+</li> <li>演讲者笔记: N</li> <li>网格背景: Enter</li> </ul>           </div><h2 id="nodeppt"><a href="#nodeppt" class="headerlink" title="nodeppt"></a>nodeppt</h2><p>首先可以看看官网给的<a href="https://nodeppt.js.org/">demo</a>，非常的炫酷。</p><iframe src="https://nodeppt.js.org/" width="100%" height="600" name="topFrame" scrolling="yes" noresize="noresize" frameborder="0" id="topFrame"></iframe><h3 id="安装nodeppt"><a href="#安装nodeppt" class="headerlink" title="安装nodeppt"></a>安装nodeppt</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ npm install -g nodeppt<br></code></pre></td></tr></table></figure><h3 id="使用nodeppt"><a href="#使用nodeppt" class="headerlink" title="使用nodeppt"></a>使用nodeppt</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># new：使用线上模板创建一个新的 md 文件</span><br><span class="hljs-comment"># create a new slide with an official template</span><br>$ nodeppt new slide.md<br><span class="hljs-comment"># 使用模板</span><br>$ nodeppt new username/repo xxx.md<br><br><span class="hljs-comment"># create a new slide straight from a github template</span><br>$ nodeppt new slide.md -t username/repo<br><br><span class="hljs-comment"># serve：启动一个 md 文件的 webpack dev server</span><br><span class="hljs-comment"># start local sever show slide</span><br>$ nodeppt serve slide.md<br><span class="hljs-comment"># start local sever show slide with port</span><br>$ nodeppt serve slide.md -p port<br><br><span class="hljs-comment"># build：编译产出一个 md 文件</span><br><span class="hljs-comment"># to build a slide</span><br>$ nodeppt build slide.md<br></code></pre></td></tr></table></figure><ul><li><p>生成的网页可以使用键盘操作(类似PPT操作)</p><ul><li>Page: ↑/↓/←/→ Space Home End</li><li>Fullscreen: F</li><li>Overview: -/+</li><li>Speaker Note: N</li><li>Grid Background: Enter</li><li>nodeppt 有演讲者模式，在页面 url 后面增加<code>?mode=speaker</code> 既可以打开演讲者模式，双屏同步</li></ul></li><li><p>端口port的好处是可以照着官网的demo文件学习和修改，保证多个slide.md在浏览器查看时不会冲突，默认的链接是<a href="http://192.168.0.105:8080/。">http://192.168.0.105:8080/。</a></p></li><li><p>官网的demo文件在<a href="https://github.com/ksky521/nodeppt/tree/master/site">Github</a>其中的<a href="https://github.com/ksky521/nodeppt/blob/master/site/index.md">index.md</a>。</p></li><li><p>产生pdf：直接在浏览器上<code>command+P/ctrl+P</code> </p></li><li><p>产生html：</p><ul><li><p>之前版本通过<code>nodeppt generate ./ppts/demo.md -a</code>，见<a href="https://github.com/zhangry868/nodePPT#html%E7%89%88">Github nodePPT v1.2.0</a></p></li><li><p>当前版本产生html利用<strong>built</strong>指令 ，例如<code>nodeppt build slide.md</code>，产生的html在默认文件夹<code>dist</code>中，包含CSS、IMG、JS三个文件夹和demo.html。</p></li><li><p>在nodeppt仓库的Issue<sup id="fnref:1" class="footnote-ref"><a href="#fn:1" rel="footnote"><span class="hint--top hint--rounded" aria-label="[分享个脚本，导出单个离线HTML文件，不是用build #289](https://github.com/ksky521/nodeppt/issues/289#issue-613221513)">[1]</span></a></sup>上找到一个小哥做的爬虫程序，亲测有效。会生成一个html文件，虽然文件会大一点。不过用<a href="https://github.com/ksky521/nodeppt/blob/master/site/index.md">index.md</a>文件实验，发现(某些)图片响应时间过长导致失败，不过自己写的markdown基本无压力转html，给小哥点大大的赞👍而且小哥表示：</p><blockquote><p>之前试过直接用build，效果没问题，但build出来会有几个文件，如果通过手机或email分享出去直接播放的话稍显麻烦。</p></blockquote></li></ul></li></ul><h3 id="nodeppt入门"><a href="#nodeppt入门" class="headerlink" title="nodeppt入门"></a>nodeppt入门</h3><ul><li>配置与hexo的post文件头一样，用 yaml 语法设定基本配置</li></ul><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">title:</span> <span class="hljs-string">nodeppt</span> <span class="hljs-string">markdown</span> <span class="hljs-string">演示</span><br><span class="hljs-attr">speaker:</span> <span class="hljs-string">三水清</span><br><span class="hljs-attr">url:</span> <span class="hljs-string">https://github.com/ksky521/nodeppt</span><br><span class="hljs-attr">js:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">https://www.echartsjs.com/asset/theme/shine.js</span><br><span class="hljs-attr">prismTheme:</span> <span class="hljs-string">solarizedlight</span><br><span class="hljs-attr">plugins:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">echarts</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">mermaid</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">katex</span><br></code></pre></td></tr></table></figure><ul><li>正文使用<code>&lt;slide&gt;</code>对整个 markdown 文件进行拆分，拆成单页的幻灯片内容。</li><li>图片、样式、布局、icon、动画等设置可以看看仓库的文档和demo文件学习。</li><li>演讲者模式的批注通过来<code>:::</code>语法添加，然后再页面的链接添加<code>?mode=speaker</code>，按<code>N</code>开启演讲中模式。</li></ul><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs markdown">:::note<br><span class="hljs-section">## Note here</span><br>:::<br></code></pre></td></tr></table></figure><h2 id="踩坑"><a href="#踩坑" class="headerlink" title="踩坑"></a>踩坑</h2><h3 id="CSS样式导入失败"><a href="#CSS样式导入失败" class="headerlink" title="CSS样式导入失败"></a>CSS样式导入失败</h3><p>生成的html数学公式的格式全部错误，即使在nodeppt的配置部分引入katex的JS和CSS，导出的文档仍然会出错。</p><ul><li>通过nodeppt build的html页面</li></ul><p>打开生成的html文件可以看到<sup id="fnref:2" class="footnote-ref"><a href="#fn:2" rel="footnote"><span class="hint--top hint--rounded" aria-label="[当在浏览器里直接打开index.html时，打开速度会非常慢 #286](https://github.com/ksky521/nodeppt/issues/286#issue-575436572)">[2]</span></a></sup></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;//cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.min.css&quot;</span> /&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;//cdn.staticfile.org/prism/1.15.0/themes/prism.min.css&quot;</span> /&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;//cdn.staticfile.org/KaTeX/0.10.0-rc.1/katex.min.css&quot;</span> /&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;//cdn.staticfile.org/KaTeX/0.5.1/katex.min.css&quot;</span> /&gt;</span><br></code></pre></td></tr></table></figure><p>只要把文件中所有<code>//</code>开头的都替换成<code>https://</code>，如</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">stylesheet</span> <span class="hljs-attr">href</span>=<span class="hljs-string">https://cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.min.css</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">stylesheet</span> <span class="hljs-attr">href</span>=<span class="hljs-string">https://cdn.staticfile.org/prism/1.15.0/themes/prism.min.css</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">stylesheet</span> <span class="hljs-attr">href</span>=<span class="hljs-string">https://cdn.staticfile.org/KaTeX/0.10.0-rc.1/katex.min.css</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">stylesheet</span> <span class="hljs-attr">href</span>=<span class="hljs-string">https://cdn.staticfile.org/KaTeX/0.5.1/katex.min.css</span>&gt;</span><br></code></pre></td></tr></table></figure><p>这样控制台就不会报错了，数学公式和fa-icon能正常显示了。</p><ul><li>通过py程序爬的html页面</li></ul><p>配合KaTeX官网的<a href="https://katex.org/docs/browser.html#starter-template">使用文档</a>，在生成的html文件<code>&lt;head&gt;</code>引用katex的JS和CSS。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span><br><span class="hljs-comment">&lt;!-- KaTeX requires the use of the HTML5 doctype. Without it, KaTeX may not render properly --&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;https://lib.baomitu.com/KaTeX/0.15.2/katex.min.css&quot;</span> <span class="hljs-attr">integrity</span>=<span class="hljs-string">&quot;sha384-zB1R0rpPzHqg7Kpt0Aljp8JPLqbXI3bhnPWROx27a9N0Ll6ZP/+DiW/UqRcLbRjq&quot;</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">&quot;anonymous&quot;</span>&gt;</span><br><br>    <span class="hljs-comment">&lt;!-- The loading of KaTeX is deferred to speed up page rendering --&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">defer</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;https://lib.baomitu.com/KaTeX/0.15.2/katex.min.js&quot;</span> <span class="hljs-attr">integrity</span>=<span class="hljs-string">&quot;sha384-y23I5Q6l+B6vatafAwxRu/0oK/79VlbSz7Q9aiSZUvyWYIYsd+qj+o24G5ZU2zJz&quot;</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">&quot;anonymous&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br><br>    <span class="hljs-comment">&lt;!-- To automatically render math in text elements, include the auto-render extension: --&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">defer</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;https://lib.baomitu.com/KaTeX/0.15.2/contrib/auto-render.min.js&quot;</span> <span class="hljs-attr">integrity</span>=<span class="hljs-string">&quot;sha384-kWPLUVMOks5AQFrykwIup5lo0m3iMkkHrD0uJ4H5cjeGihAutqP0yW0J6dpFiVkI&quot;</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">&quot;anonymous&quot;</span></span><br><span class="hljs-tag">        <span class="hljs-attr">onload</span>=<span class="hljs-string">&quot;renderMathInElement(document.body);&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span><br>  ...<br><span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span><br></code></pre></td></tr></table></figure><p>添加后公式都能正确显示了。</p><h3 id="在博客添加nodeppt"><a href="#在博客添加nodeppt" class="headerlink" title="在博客添加nodeppt"></a>在博客添加nodeppt</h3><ul><li>通过py程序爬的html页面</li></ul><p>在Hexo博客里想调用或者链接nodeppt生成的html，需要hexo设置<code>skip_render</code>, 指定不进行渲染的文件或文件夹，例如在<code>source</code>目录下新建<code>nodeppt</code>来存放nodeppt生成的html，则需要在根目录下的<code>_config.yml</code>文件添加</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">skip_render:</span> <br>  <span class="hljs-bullet">-</span> <span class="hljs-string">nodeppt/*.html</span><br></code></pre></td></tr></table></figure><ul><li>通过nodeppt build的html页面</li></ul><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">skip_render:</span> <br>  <span class="hljs-bullet">-</span> <span class="hljs-string">nodeppt/**</span><br></code></pre></td></tr></table></figure><p>文件匹配是基于正则匹配的，如果需要忽略全部文件(<code>/*</code>)、指定类型type文件(<code>/*.type</code>)、全部文件以及子目录(<code>/**</code>)以及多个文件需要用(<code>- file/**</code>)。</p><p>对应的文件访问格式是<code>../../nodeppt/file.html</code>或<code>../../nodeppt/file/demo.html</code>，本页演示的加载是通过<code>iframe</code>实现的。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">iframe</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;../../nodeppt/file.html&quot;</span> <span class="hljs-attr">width</span>=<span class="hljs-string">&quot;100%&quot;</span> <span class="hljs-attr">height</span>=<span class="hljs-string">&quot;500&quot;</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;topFrame&quot;</span> <span class="hljs-attr">scrolling</span>=<span class="hljs-string">&quot;yes&quot;</span> <span class="hljs-attr">noresize</span>=<span class="hljs-string">&quot;noresize&quot;</span> <span class="hljs-attr">frameborder</span>=<span class="hljs-string">&quot;0&quot;</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;topFrame&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">iframe</span>&gt;</span><br></code></pre></td></tr></table></figure><p>注意：如果这一步不执行的话，debug会发现nodeppt生成的html会被hexo处理，产生错误</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">FATAL Something<span class="hljs-string">&#x27;s wrong. Maybe you can find the solution here: https://hexo.io/docs/troubleshooting.html</span><br><span class="hljs-string">Nunjucks Error:  [Line 9418, Column 3465] expected variable end</span><br></code></pre></td></tr></table></figure><p>至于使用cdn来使用html似乎不行，出来的是html的源码，而不是网页。如果使用cdn的方式能成功就不用这么麻烦的<code>skip_render</code>。</p><p>有一种简单的方法就是用github或者coding等部署nodeppt的html，再iframe的src填对应的网址。如果hexo的<code>skip_render</code>设置正确，也可通过网址主页下的nodeppt下找到。</p><ul><li><a href="https://pxxyyz.com/nodeppt/%E5%A4%9A%E5%A4%8D%E5%8F%98%E8%BF%91%E6%9C%9F%E8%BF%9B%E5%B1%95/demo.html">https://pxxyyz.com/nodeppt/%E5%A4%9A%E5%A4%8D%E5%8F%98%E8%BF%91%E6%9C%9F%E8%BF%9B%E5%B1%95/demo.html</a></li><li><a href="http://uwrfy5.coding-pages.com/">http://uwrfy5.coding-pages.com/</a></li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1" class="footnote-text"><span><a href="https://github.com/ksky521/nodeppt/issues/289#issue-613221513">分享个脚本，导出单个离线HTML文件，不是用build #289</a><a href="#fnref:1" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:2" class="footnote-text"><span><a href="https://github.com/ksky521/nodeppt/issues/286#issue-575436572">当在浏览器里直接打开index.html时，打开速度会非常慢 #286</a><a href="#fnref:2" rev="footnote" class="footnote-backref"> ↩</a></span></span></li></ol></div></section>]]></content>
    
    
    <categories>
      
      <category>功能增强</category>
      
    </categories>
    
    
    <tags>
      
      <tag>Hexo</tag>
      
      <tag>用户经验</tag>
      
      <tag>花里胡哨</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>centos7下编译安装postgis3</title>
    <link href="/blog/posts/a70e035f.html"/>
    <url>/blog/posts/a70e035f.html</url>
    
    <content type="html"><![CDATA[<p>本文认为已安装PostgreSQL12，安装步骤如 <a href="https://www.jianshu.com/p/639ebb43bfb4">Centos7安装PostgreSQL</a>，最好按照前文先把pg安装好，否则，在postgis,pgrouting安装时，指定pg的安装目录，直接抄路径应该不对，读者要指向自己的安装位置等。</p><span id="more"></span><h2 id="安装前准备"><a href="#安装前准备" class="headerlink" title="安装前准备"></a>安装前准备</h2><h3 id="升级cmake"><a href="#升级cmake" class="headerlink" title="升级cmake"></a>升级cmake</h3><p>CGAL4-11。因为4-11需要CMake3.11以上</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">cmake -version<br><span class="hljs-meta">#</span><span class="bash"> cmake version 2.8.12.2</span><br></code></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs shell">wget https://github.com/Kitware/CMake/releases/download/v3.16.8/cmake-3.16.8.tar.gz<br>tar -zxvf cmake-3.16.8.tar.gz<br>cd cmake-3.16.8<br>./bootstrap --prefix=/usr/local/cmake<br>gamke<br>gmake install<br></code></pre></td></tr></table></figure><p><code>postgis</code>还要安装其他依赖，比如<code>GEOS</code>,<code>proj</code>,<code>GDAL</code>，各版本需要的依赖版本，<a href="http://postgis.net/news/">http://postgis.net/news/</a>  ，如图。</p><img src="/blog/posts/a70e035f/image-20200612124515120.png" class="" title="image-20200612124515120"><p>本文基于GEOS 3.8.1，PROJ 6.3.2，GDAL 3.0.4，json-c 0.13.1，CGAL4.14.3，SFCGAL1.3.7，protobuf3.11.4，protobuf-c1.3.3，libxml2-2.9.10，pcre-8.44，PostGIS-3.0.1,pgrouting3.0.2安装。</p><h2 id="安装依赖或插件"><a href="#安装依赖或插件" class="headerlink" title="安装依赖或插件"></a>安装依赖或插件</h2><h3 id="安装GEOS-3-8-1"><a href="#安装GEOS-3-8-1" class="headerlink" title="安装GEOS-3.8.1"></a>安装GEOS-3.8.1</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs shell">wget https://download.osgeo.org/geos/geos-3.8.1.tar.bz2<br>tar -jxf geos-3.8.1.tar.bz2<br>cd geos-3.8.1<br>./configure --prefix=/usr/local/geos-3.8.1<br>make &amp;&amp; make install<br></code></pre></td></tr></table></figure><p>:blush:  </p><h3 id="安装proj-6-3-2"><a href="#安装proj-6-3-2" class="headerlink" title="安装proj-6.3.2"></a>安装proj-6.3.2</h3><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs sh">wget http://download.osgeo.org/proj/proj-6.3.2.tar.gz<br>tar -zxvf proj-6.3.2.tar.gz<br><span class="hljs-built_in">cd</span> proj-6.3.2<br>./configure  --prefix=/usr/<span class="hljs-built_in">local</span>/proj-6.3.2<br><span class="hljs-comment"># 编译时遇到下面的问题，说是sqlite版本太低！编译成功再进行下一步！</span><br>make &amp;&amp; make install<br></code></pre></td></tr></table></figure><p>遇到如图片的问题！</p><img src="/blog/posts/a70e035f/image-20200613092144891.png" class="" title="image-20200613092144891"><p>然后我在命令行输入sqlite3的时候，结果是3.22，哪来的3.7.17？</p><p><a href="https://stackoverflow.com/questions/62154342/configure-error-package-requirements-sqlite3-3-7-4-were-not-met">https://stackoverflow.com/questions/62154342/configure-error-package-requirements-sqlite3-3-7-4-were-not-met</a></p><img src="/blog/posts/a70e035f/image-20200613092614714.png" class="" title="image-20200613092614714"><p>原来是sqlite-devel的版本，但我查询之后是最新的！！于是我删了sqlite-devl，得到的是<code>Checking for module &#39;sqlite3&#39; No package &#39;sqlite3&#39; found</code></p><p>暴力升级也没有用，不得不看日志的下半部分。于是搜索关键词<code>PKG_CONFIG_PATH</code>。找到<a href="https://my.oschina.net/zzop/blog/499908这篇文章，其中说到：">https://my.oschina.net/zzop/blog/499908这篇文章，其中说到：</a></p><img src="/blog/posts/a70e035f/image-20200613093520041.png" class="" title="image-20200613093520041"><p>:point_right:</p><img src="/blog/posts/a70e035f/image-20200613094337927.png" class="" title="image-20200613094337927"><p><code>cp /usr/local/lib/pkgconfig/sqlite3.pc /usr/lib64/pkgconfig/</code></p><p>重新编译就好了！</p><img src="/blog/posts/a70e035f/image-20200613095115674.png" class="" title="image-20200613095115674"><h3 id="安装GDAL"><a href="#安装GDAL" class="headerlink" title="安装GDAL"></a>安装GDAL</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs shell">wget https://download.osgeo.org/gdal/3.0.4/gdal-3.0.4.tar.gz<br>tar -zxvf gdal-3.0.4.tar.gz <br>cd gdal-3.0.4 <br><span class="hljs-meta">#</span><span class="bash"> 带pg的配置，具体需要啥可以./configure --<span class="hljs-built_in">help</span> 查看</span><br>./configure  --prefix=/usr/local/gdal-3.0.4 --with-pg=yes<br>make<br><span class="hljs-meta">#</span><span class="bash"> 编译时遇到下面的问题，成功则进行下一步！</span><br>make install<br></code></pre></td></tr></table></figure><p>遇到</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs shell">collect2: error: ld returned 1 exit status<br>make[1]: *** [GNUmakefile:82: gdalinfo] Error 1<br>make: *** [GNUmakefile:112: apps-target] Error 2<br></code></pre></td></tr></table></figure><p>解决办法可能是<code>make clean</code>,紧接着按之前的编译安装！</p><p>参考文章：<a href="https://stackoverflow.com/questions/60218227/trying-to-install-gdal-3-0-4-on-red-hat-8">https://stackoverflow.com/questions/60218227/trying-to-install-gdal-3-0-4-on-red-hat-8</a></p><p>我的编译配置：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sh">./configure --prefix=/usr/<span class="hljs-built_in">local</span>/gdal-3.0.4 --with-proj=/usr/<span class="hljs-built_in">local</span>/proj-6.3.2 --with-geos=/usr/<span class="hljs-built_in">local</span>/geos-3.8.1/bin/geos-config --with-sqlite3=/usr/<span class="hljs-built_in">local</span>/bin/sqlite3 --with-libjson-c=/usr/<span class="hljs-built_in">local</span>/json-c-0.13.1 --with-pg=yes --with-python=/root/.virtualenvs/aigisss_py/bin/python3.6 <br></code></pre></td></tr></table></figure><h3 id="安装protubuf"><a href="#安装protubuf" class="headerlink" title="安装protubuf"></a>安装protubuf</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs shell">tar -xzvf  protobuf-cpp-3.11.4.tar.gz<br>./configure  --prefix=/usr/local/protobuf-3.11.4<br>make &amp;&amp; make install<br></code></pre></td></tr></table></figure><p>竟然说<code>bash: ./configure: No such file or directory</code></p><p>解决方案</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">yum install automake<br>autoreconf -i<br></code></pre></td></tr></table></figure><p>参考文章：<a href="https://stackoverflow.com/questions/24054761/configure-gives-error-in-ubuntu">https://stackoverflow.com/questions/24054761/configure-gives-error-in-ubuntu</a></p><p>出现编译错误</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs shell">make[2]: *** [message.lo] Error 1 <br>make[1]: *** [all-recursive] Error 1<br>make: *** [all] Error 2<br></code></pre></td></tr></table></figure><p>解决方案下载含有all的资源包</p><img src="/blog/posts/a70e035f/image-20200614185029679.png" class="" title="image-20200614185029679"><p>参考文章：<a href="https://github.com/protocolbuffers/protobuf/issues/6599">https://github.com/protocolbuffers/protobuf/issues/6599</a></p><h3 id="安装protobuf-c"><a href="#安装protobuf-c" class="headerlink" title="安装protobuf-c"></a>安装protobuf-c</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs shell">wget https://github.com/protobuf-c/protobuf-c/releases/download/v1.3.3/protobuf-c-1.3.3.tar.gz<br>tar -xzvf  protobuf-c-1.3.3.tar.gz<br>cd protobuf-c-1.3.3<br>export PKG_CONFIG_PATH=/usr/local/protobuf-3.11.4/lib/pkgconfig<br>./configure  --prefix=/usr/local/protobuf-c-1.3.3<br>make &amp;&amp; make install<br></code></pre></td></tr></table></figure><h3 id="安装SFCGAL-1-3-7"><a href="#安装SFCGAL-1-3-7" class="headerlink" title="安装SFCGAL 1.3.7"></a>安装SFCGAL 1.3.7</h3><p>由于SFCGAL需要依赖Boost、CGAL、GMP、MPFR这四个软件，所以具体总共需要安装以下四个软件：</p><ol><li><p>boost-devel.x86_64</p></li><li><p>gmp-devel.x86_64</p></li><li><p>mpfr-devel.x86_64</p></li><li><p>CGAL-4.14</p><p>为了安装<code>pgrouting3.0.2</code>需要安装boost<code>1.53</code>以上,使用<code>yum install boost boost-devel</code>只能安装版本<code>1.53</code>, 我使用源码安装的是<code>1.68</code></p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs sh">wget https://dl.bintray.com/boostorg/release/1.68.0/<span class="hljs-built_in">source</span>/boost_1_68_0.tar.gz<br>tar -xzvf boost_1_68_0.tar.gz<br><span class="hljs-built_in">cd</span> boost_1_68_0<br>./bootstrap.sh <br>./b2 install --with=all<br></code></pre></td></tr></table></figure><ul><li>参考:<a href="https://stackoverflow.com/questions/33050113/how-to-install-boost-devel-1-59-in-centos7">https://stackoverflow.com/questions/33050113/how-to-install-boost-devel-1-59-in-centos7</a></li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">yum install gmp-devel.x86_64<br>yum install mpfr-devel.x86_64<br></code></pre></td></tr></table></figure></li></ol><h3 id="安装CGAL"><a href="#安装CGAL" class="headerlink" title="安装CGAL"></a>安装CGAL</h3><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs sh">wget http://distfiles.macports.org/cgal/cgal-4.14.3.tar.xz<br>xz -d CGAL-4.14.3.tar.xz<br>tar -xvf  CGAL-4.14.3.tar<br><span class="hljs-built_in">cd</span> CGAL-4.14.3<br>mkdir build &amp;&amp; <span class="hljs-built_in">cd</span> build<br>cmake ..<br>make &amp;&amp; make install<br></code></pre></td></tr></table></figure><h3 id="安装pcre"><a href="#安装pcre" class="headerlink" title="安装pcre"></a>安装pcre</h3><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs sh">wget https://ftp.pcre.org/pub/pcre/pcre-8.44.tar.gz<br>tar -xzvf  pcre-8.44.tar.gz<br><span class="hljs-built_in">cd</span> pcre-8.44<br>./configure --enable-utf8 --prefix=/usr/<span class="hljs-built_in">local</span>/pcre-8.44<br>make &amp;&amp; make intall<br><span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;/usr/local/pcre/lib&quot;</span> &gt; /etc/ld.so.conf.d/pcre-8.44.conf<br>ldconfig<br></code></pre></td></tr></table></figure><h3 id="安装SFCGAL"><a href="#安装SFCGAL" class="headerlink" title="安装SFCGAL"></a>安装SFCGAL</h3><p>遇到以下这个问题：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sh">c++: internal compiler error: Killed (program cc1plus)<br>Please submit a full bug report<br></code></pre></td></tr></table></figure><p>解决方案：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs sh">sudo dd <span class="hljs-keyword">if</span>=/dev/zero of=/swapfile bs=64M count=16<br><span class="hljs-comment">#count的大小就是增加的swap空间的大小，64M是块大小，所以空间大小是bs*count=1024MB</span><br>sudo mkswap /swapfile<br><span class="hljs-comment">#把刚才空间格式化成swap格式</span><br>chmod 0600 /swapfile  <br><span class="hljs-comment">#该目录权限，不改的话，在下一步启动时会报“swapon: /swapfile: insecure permissions 0644, 0600 suggested.”错误</span><br>sudo swapon /swapfile<br><span class="hljs-comment">#使用刚才创建的swap空间</span><br></code></pre></td></tr></table></figure><p>安装编译：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs shell">wget  https://github.com/Oslandia/SFCGAL/archive/v1.3.7.tar.gz<br>tar -zxvf SFCGAL-1.3.7.tar.gz<br>cd SFCGAL-1.3.7  <br>mkdir build &amp;&amp; cd build <br>cmake -DCMAKE_INSTALL_PREFIX=/usr/local/sfcgal-1.3.7 ..<br>make  &amp;&amp; make install<br></code></pre></td></tr></table></figure><p>装完之后释放空间：</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs sh">swapoff -a<br><span class="hljs-comment">#详细的用法可以：swapoff --help</span><br><span class="hljs-comment">#查看当前内存使用情况：free -m</span><br></code></pre></td></tr></table></figure><p>参考文章：<a href="https://blog.csdn.net/qq_27148893/article/details/88936044">https://blog.csdn.net/qq_27148893/article/details/88936044</a></p><h2 id="安装PostGIS"><a href="#安装PostGIS" class="headerlink" title="安装PostGIS"></a>安装PostGIS</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">./configure --prefix=/usr/local/postgis-3.0.1 --with-gdalconfig=/usr/local/gdal-3.0.4/bin/gdal-config --with-pgconfig=/opt/pg12/bin/pg_config --with-geosconfig=/usr/local/geos-3.8.1/bin/geos-config --with-projdir=/usr/local/proj-6.3.2 --with-xml2config=/usr/local/libxml2-2.9.10/bin/xml2-config --with-jsondir=/usr/local/json-c-0.13.1 --with-protobufdir=/usr/local/protobuf-c-1.3.3 --with-sfcgal=/usr/local/sfcgal-1.3.7/bin/sfcgal-config --with-pcredir=/usr/local/pcre-8.44<br></code></pre></td></tr></table></figure><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs sh">PG_HOME=/opt/pg12<br><br>LD_LIBRARY_PATH=<span class="hljs-variable">$PG_HOME</span>/lib:<span class="hljs-variable">$LD_LIBRARY_PATH</span><br>PATH=<span class="hljs-variable">$PG_HOME</span>/bin:<span class="hljs-variable">$PATH</span><br><br>PKG_CONFIG_PATH=<span class="hljs-variable">$PG_HOME</span>/lib/pkgconfig:<span class="hljs-variable">$PKG_CONFIG_PATH</span><br><br><span class="hljs-built_in">export</span> CMAKE_HOME=/usr/bin/cmake<br><span class="hljs-built_in">export</span> PROTOBUF_HOME=/usr/<span class="hljs-built_in">local</span>/protobuf-3.11.4<br><br>GDAL_HOME=/usr/<span class="hljs-built_in">local</span>/gdal-3.0.4<br>GDAL_DATA=<span class="hljs-variable">$GDAL_HOME</span>/share/gdal<br>LD_LIBRARY_PATH=<span class="hljs-variable">$GDAL_HOME</span>/lib:/usr/<span class="hljs-built_in">local</span>/lib64:<span class="hljs-variable">$JRE_HOME</span>/lib:<span class="hljs-variable">$LD_LIBRARY_PATH</span><br>PATH=<span class="hljs-variable">$GDAL_HOME</span>/bin:<span class="hljs-variable">$PATH</span><br><br><span class="hljs-built_in">export</span> PATH=<span class="hljs-variable">$CMAKE_HOME</span>/bin:<span class="hljs-variable">$PROTOBUF_HOME</span>/bin:/usr/<span class="hljs-built_in">local</span>/protobuf-c-1.3.3/bin:<span class="hljs-variable">$PATH</span><br><span class="hljs-built_in">export</span> PKG_CONFIG_PATH LD_LIBRARY_PATH<br><span class="hljs-built_in">export</span> LD_LIBRARY_PATH GDAL_DATA<br></code></pre></td></tr></table></figure><h2 id="创建postgis扩展"><a href="#创建postgis扩展" class="headerlink" title="创建postgis扩展"></a>创建postgis扩展</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs shell">su - postgres<br>psql<br>create database gistest;<br>\c gistest<br>create extension postgis;<br><span class="hljs-meta">#</span><span class="bash">如果安装了sfcgal，创建扩展测试下</span><br>create extension postgis_sfcgal;<br></code></pre></td></tr></table></figure><h2 id="安装PgRouting"><a href="#安装PgRouting" class="headerlink" title="安装PgRouting"></a>安装PgRouting</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs shell">wget  https://github.com/pgRouting/pgrouting/releases/download/v3.0.2/pgrouting-3.0.2.tar.gz<br>tar -zxvf pgrouting-3.0.2.tar.gz<br>cd pgrouting-3.0.2<br>mkdir build &amp;&amp; cd build <br><span class="hljs-meta">#</span><span class="bash">引入postgres的环境变量</span><br>source /home/postgres/.bashrc<br>cmake ..<br>make<br>make install<br></code></pre></td></tr></table></figure><p>验证安装:</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs sh">su - postgres<br>psql<br>create database gistest;<br>\c gistest<br>create extension pgrouting;<br></code></pre></td></tr></table></figure><p>验证:sql查询</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> ST_AsX3D(ST_Extrude(ST_Buffer(ST_GeomFromText(<span class="hljs-string">&#x27;POINT(100 90)&#x27;</span>), <span class="hljs-number">50</span>, <span class="hljs-string">&#x27;quad_segs=2&#x27;</span>),<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">30</span>));<br></code></pre></td></tr></table></figure><img src="/blog/posts/a70e035f/image-20200718223155807.png" class="" title="image-20200718223155807"><p>以上就完成安装了!!</p><blockquote><p>参考文章：</p><ul><li><a href="https://www.jianshu.com/p/e08dbc60a3b2">https://www.jianshu.com/p/e08dbc60a3b2</a></li><li><a href="https://www.jianshu.com/p/ff4cf2b59613">https://www.jianshu.com/p/ff4cf2b59613</a></li><li><a href="https://www.jianshu.com/p/f9b6a498db55">https://www.jianshu.com/p/f9b6a498db55</a></li></ul></blockquote>]]></content>
    
    
    
    <tags>
      
      <tag>Postgis</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>在 Fluid 主题首页上加入一言</title>
    <link href="/blog/posts/9604e2c8.html"/>
    <url>/blog/posts/9604e2c8.html</url>
    
    <content type="html"><![CDATA[<div class="note note-success">            <p>本文由 Fluid 用户授权转载，版权归原作者所有。</p> <p>本文作者：pxxyyz</p> <p>原文地址：<a href="https://pxxyyz.com/posts/30454/">https://pxxyyz.com/posts/30454/</a></p>           </div><p>群里有个小哥想在首页 Slogan 上显示一言，在 GitHub 上搜了搜，还真有 Fluid 主题的改造，我按照思路写下改造的步骤。</p><h2 id="修改代码"><a href="#修改代码" class="headerlink" title="修改代码"></a>修改代码</h2><h3 id="typed-ejs"><a href="#typed-ejs" class="headerlink" title="typed.ejs"></a>typed.ejs</h3><p>修改<code>layout\_partial\plugins</code>目录下的<code>typed.ejs</code></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><code class="hljs html">&lt;% if(theme.fun_features.typing.enable &amp;&amp; page.subtitle !== false)&#123; %&gt;<br>  &lt;%- js_ex(theme.static_prefix.typed, &quot;/typed.min.js&quot;) %&gt;<br>  <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript"></span><br><span class="javascript">    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">typing</span>(<span class="hljs-params">id, title</span>)</span>&#123;</span><br><span class="javascript">        <span class="hljs-keyword">var</span> typed = <span class="hljs-keyword">new</span> Typed(<span class="hljs-string">&#x27;#&#x27;</span> + id, &#123;</span><br><span class="javascript">            <span class="hljs-attr">strings</span>: [</span><br><span class="javascript">              <span class="hljs-string">&#x27;  &#x27;</span>,</span><br><span class="javascript">              title + <span class="hljs-string">&quot;&amp;nbsp;&quot;</span>,</span><br><span class="javascript">            ],</span><br><span class="javascript">            <span class="hljs-attr">cursorChar</span>: <span class="hljs-string">&quot;&lt;%- theme.fun_features.typing.cursorChar %&gt;&quot;</span>,</span><br><span class="javascript">            <span class="hljs-attr">typeSpeed</span>: &lt;%- theme.fun_features.typing.typeSpeed %&gt;,</span><br><span class="javascript">            loop: &lt;%- theme.fun_features.typing.loop %&gt;,</span><br><span class="javascript">        &#125;);</span><br><span class="javascript">        typed.stop();</span><br><span class="javascript">        $(<span class="hljs-built_in">document</span>).ready(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;</span><br><span class="javascript">            $(<span class="hljs-string">&quot;.typed-cursor&quot;</span>).addClass(<span class="hljs-string">&quot;h2&quot;</span>);</span><br><span class="javascript">            typed.start();</span><br><span class="javascript">        &#125;);</span><br><span class="javascript">    &#125;</span><br><span class="javascript">    &lt;% <span class="hljs-keyword">if</span>(is_post()) &#123; %&gt;</span><br><span class="javascript">        typing(<span class="hljs-string">&quot;subtitle&quot;</span>, <span class="hljs-string">&quot;&lt;%- data.subtitle %&gt;&quot;</span>)</span><br><span class="javascript">    &lt;% &#125; <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(theme.index.hitokoto.enable)&#123; %&gt;</span><br><span class="javascript">        fetch(<span class="hljs-string">&#x27;https://v1.hitokoto.cn&#x27;</span>)</span><br><span class="javascript">        .then(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> response.json())</span><br><span class="javascript">        .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> &#123;</span><br><span class="javascript">           typing(<span class="hljs-string">&quot;hitokoto&quot;</span>, data.hitokoto)</span><br><span class="javascript">        &#125;)</span><br><span class="javascript">        .catch(<span class="hljs-built_in">console</span>.error)</span><br><span class="javascript">    &lt;% &#125; <span class="hljs-keyword">else</span> &#123; %&gt;</span><br><span class="javascript">        typing(<span class="hljs-string">&quot;subtitle&quot;</span>, <span class="hljs-string">&quot;&lt;%- data.subtitle %&gt;&quot;</span>)</span><br><span class="javascript">    &lt;% &#125; %&gt;</span><br><span class="javascript">  </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br>&lt;% &#125; %&gt;<br></code></pre></td></tr></table></figure><ul><li>将原来的功能放在typing函数里面，再判断打字机显示subtitle还是hitokoto<ul><li>所有的post都显示subtitle，即markdown的title，page的title是网站的标题</li><li>除了post以外，判断<code>theme.index.hitokoto.enable</code><ul><li>设置显示一言，则通过fetch调用hitokoto的API，这个部分官方说明<sup id="fnref:1" class="footnote-ref"><a href="#fn:1" rel="footnote"><span class="hint--top hint--rounded" aria-label="[一言开发者中心](https://developer.hitokoto.cn/)">[1]</span></a></sup>和DIY教程<sup id="fnref:2" class="footnote-ref"><a href="#fn:2" rel="footnote"><span class="hint--top hint--rounded" aria-label="[为您的Hexo博客添加Hitokoto一言功能](https://blog.bill.moe/add-hitokoto/)">[2]</span></a></sup>都说的很详细了</li><li>如果设置不显示一言，则显示subtitle</li></ul></li><li>hitokoto比subtitle优先级高，这会导致归档、分类、标签等页面的打字机显示hitokoto</li><li>如果只需要在首页显示hitokoto，但在非post的页面显示原subtitle，这需要判断页面的属性，据我观察，所有的非post的页面布局(layout)都会设置<code>page.layout=”XXX“</code>，但是index和page没有设置，因此，可以通过<code>!page.layout</code>判断来判断是否为首页，当然，post页面设定显示subtitle，就不在考虑范围内，这样只需将上面的<code>else if</code>条件修改如下</li></ul></li></ul><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs html">&lt;% &#125; else if(theme.index.hitokoto.enable &amp;&amp; !page.layout) &#123; %&gt;<br></code></pre></td></tr></table></figure><h3 id="layout-ejs"><a href="#layout-ejs" class="headerlink" title="layout.ejs"></a>layout.ejs</h3><p>修改<code>layout</code>目录下的<code>layout.ejs</code>，在<code>&lt;span class=&quot;h2&quot; id=&quot;subtitle&quot;&gt;</code>和<code>&lt;% if(is_post()) &#123; %&gt;</code>之间插入如下代码</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;h2&quot;</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;subtitle&quot;</span>&gt;</span><br>    &lt;% if(theme.fun_features.typing.enable == false) &#123; %&gt;<br>    &lt;%- subtitle %&gt;<br>    &lt;% &#125; %&gt;<br><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><br><br>&lt;% if(!is_post()) &#123; %&gt;<br><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;h2&quot;</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;hitokoto&quot;</span>&gt;</span><br>    &lt;% if(theme.fun_features.typing.enable == false) &#123; %&gt;<br>    &lt;%- hitokoto %&gt;<br>    &lt;% &#125; %&gt;<br><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><br>&lt;% &#125; %&gt;<br><br>&lt;% if(is_post()) &#123; %&gt;<br>&lt;%- partial(&#x27;_partial/post-meta&#x27;) %&gt;<br>&lt;% &#125; %&gt;<br><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br></code></pre></td></tr></table></figure><p>这个部分设置显示hitokoto的样式和位置，不设置这个会报关于<code>typing(&quot;hitokoto&quot;, data.hitokoto)</code>的错误</p><figure class="highlight crmsh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs crmsh">TypeError: Cannot <span class="hljs-keyword">read</span> <span class="hljs-keyword">property</span><span class="hljs-title"> </span>&#x27;tagName&#x27; of null<br></code></pre></td></tr></table></figure><h2 id="修改配置"><a href="#修改配置" class="headerlink" title="修改配置"></a>修改配置</h2><p>在<code>source\_data</code>目录下修改<strong>主题配置</strong>文件<code>fluid_config.yml</code>，在<code>index</code>下设置hitokoto的开关</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment">#---------------------------</span><br><span class="hljs-comment"># 首页</span><br><span class="hljs-comment"># Index Page</span><br><span class="hljs-comment">#---------------------------</span><br><span class="hljs-attr">index:</span><br><span class="hljs-comment"># 添加hitokoto</span><br>  <span class="hljs-attr">slogan:</span>  <span class="hljs-comment"># 首页副标题的独立设置</span><br>    <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span>  <span class="hljs-comment"># 为 false 则不显示任何内容</span><br>    <span class="hljs-attr">text:</span> <span class="hljs-string">&#x27;More haste, less speed.&#x27;</span>  <span class="hljs-comment"># 为空则按 hexo config.subtitle 显示</span><br>  <span class="hljs-attr">hitokoto:</span>  <span class="hljs-comment"># 非post页面显示一言</span><br>    <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span>  <span class="hljs-comment"># slogan 和 hitokoto 不能同时启用，优先显示hitokoto</span><br></code></pre></td></tr></table></figure><ul><li>当<code>theme.index.hitokoto.enable == true</code>时，slogan里的text不在显示，因此只有关闭hitokoto才能在首页显示slogan的text或页面的subtitle</li></ul><h2 id="加入出处"><a href="#加入出处" class="headerlink" title="加入出处"></a>加入出处</h2><ol><li>如果想加入出处，可在打印<code>data.hitokoto</code>后加入<code>data.from</code>，以及相应的格式</li></ol><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs html">typing(&quot;hitokoto&quot;, &#x27;『&#x27; + data.hitokoto + &#x27;』&#x27; + &#x27;<span class="hljs-tag">&lt;<span class="hljs-name">br</span> /&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">h5</span>&gt;</span>&#x27;+ &#x27;——&#x27; + &#x27;「&#x27; + data.from + &#x27;」&#x27; + &#x27;<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>&#x27;)<br></code></pre></td></tr></table></figure><ol><li>另一种显示出处的方法是另起一行打印<code>data.from</code>，</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs js">fetch(<span class="hljs-string">&#x27;https://v1.hitokoto.cn&#x27;</span>)<br>.then(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> response.json())<br>.then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> &#123;<br>    typing(<span class="hljs-string">&quot;hitokoto&quot;</span>, data.hitokoto)<br>    typing(<span class="hljs-string">&quot;hitofrom &quot;</span>, data.from)<br>&#125;)<br>.catch(<span class="hljs-built_in">console</span>.error)<br></code></pre></td></tr></table></figure><ul><li>并在<code>layout.ejs</code>添加<code>&lt;%- hitofrom %&gt;</code></li></ul><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs html">&lt;% if(!is_post()) &#123; %&gt;<br><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;h2&quot;</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;hitokoto&quot;</span>&gt;</span><br>    &lt;% if(theme.fun_features.typing.enable == false) &#123; %&gt;<br>    &lt;%- hitokoto %&gt;<br>    &lt;% &#125; %&gt;<br><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;h2&quot;</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;hitofrom&quot;</span>&gt;</span><br>    &lt;% if(theme.fun_features.typing.enable == false) &#123; %&gt;<br>    &lt;%- hitofrom %&gt;<br>    &lt;% &#125; %&gt;<br><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><br>&lt;% &#125; %&gt;<br></code></pre></td></tr></table></figure><ul><li>第一种是打印一段话，从头到尾只有一个cursorChar，但样式不太好改</li><li>第二种是打印两段话，会出现视觉混乱(个人觉得)，样式方便调整</li></ul><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>当然这个还可以继续改下去，例如添加出处(hitofrom)、设置循环(loop)、修改样式等。</p><p>最后也是最重要的，感谢tanxinzheng<sup id="fnref:3" class="footnote-ref"><a href="#fn:3" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Code World](http://tanxinzheng.github.io/)">[3]</span></a></sup> (虽然不认识，但是新知识get！😁)。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1" class="footnote-text"><span><a href="https://developer.hitokoto.cn/">一言开发者中心</a><a href="#fnref:1" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:2" class="footnote-text"><span><a href="https://blog.bill.moe/add-hitokoto/">为您的Hexo博客添加Hitokoto一言功能</a><a href="#fnref:2" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:3" class="footnote-text"><span><a href="http://tanxinzheng.github.io/">Code World</a><a href="#fnref:3" rev="footnote" class="footnote-backref"> ↩</a></span></span></li></ol></div></section>]]></content>
    
    
    <categories>
      
      <category>功能增强</category>
      
    </categories>
    
    
    <tags>
      
      <tag>用户经验</tag>
      
      <tag>花里胡哨</tag>
      
      <tag>Fluid</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>Hexo 云服务备份与使用 Jupyter</title>
    <link href="/blog/posts/9604e2c2.html"/>
    <url>/blog/posts/9604e2c2.html</url>
    
    <content type="html"><![CDATA[<div class="note note-success">            <p>本文由 Fluid 用户授权转载，版权归原作者所有。</p> <p>本文作者：pxxyyz</p> <p>原文地址：<a href="https://pxxyyz.com/posts/32990/">https://pxxyyz.com/posts/32990/</a>、<a href="https://pxxyyz.com/posts/60533/">https://pxxyyz.com/posts/60533/</a></p>           </div><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>记录hexo博客遇到的问题：</p><ul><li>免密git</li><li>自动备份</li><li>云服务器开启<code>Jupyter Notebook</code></li><li>在博客的菜单访问<code>Jupyter</code>（使用<code>Nginx</code>重定向实现url访问端口）</li><li>公式渲染引擎</li></ul><h2 id="云服务器备份"><a href="#云服务器备份" class="headerlink" title="云服务器备份"></a>云服务器备份</h2><p>参考「Hexo博客部署到腾讯云服务器」<sup id="fnref:1" class="footnote-ref"><a href="#fn:1" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Hexo博客部署到腾讯云服务器](https://www.muyiio.com/2020/03/28/hexo-bo-ke-bu-shu-dao-teng-xun-yun-fu-wu-qi/)">[1]</span></a></sup>后遇到了两个问题：</p><ol><li><p>每次在本地部署博客时都要重复输入密码</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">deploy:</span><br>    <span class="hljs-attr">type:</span> <span class="hljs-string">git</span><br>    <span class="hljs-attr">repo:</span> <span class="hljs-string">root@***(服务器ip,内网外网都行):/home/git/blog.git</span>    <span class="hljs-comment">#仓库地址</span><br>    <span class="hljs-attr">branch:</span> <span class="hljs-string">master</span>    <span class="hljs-comment">#分支</span><br></code></pre></td></tr></table></figure></li><li><p>备份时hexo自带的backup无效</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">backup:</span><br>    <span class="hljs-attr">type:</span> <span class="hljs-string">git</span><br>    <span class="hljs-attr">repo:</span> <span class="hljs-string">root@***(服务器ip,内网外网都行):/home/git/backup.git</span>    <span class="hljs-comment">#仓库地址</span><br>    <span class="hljs-attr">branch:</span> <span class="hljs-string">master</span>    <span class="hljs-comment">#分支</span><br></code></pre></td></tr></table></figure></li></ol><h3 id="免密git"><a href="#免密git" class="headerlink" title="免密git"></a>免密git</h3><p>在这一部分参照了「使用Git+Hooks实现Hexo站点自动部署到CentOS服务器上」<sup id="fnref:2" class="footnote-ref"><a href="#fn:2" rel="footnote"><span class="hint--top hint--rounded" aria-label="[使用Git+Hooks实现Hexo站点自动部署到CentOS服务器上](https://liujunzhou.top/deployer/)">[2]</span></a></sup>的配置SSH免密登陆步骤。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ ssh-copy-id -i ~/.ssh/id_rsa.pub your_user_name@HostIP  //添加公钥<br>$ ssh your_user_name@HostIP //验证是否添加成功<br></code></pre></td></tr></table></figure><p>因为部署时用的root上传，因此这里的<code>your_user_name</code>设置<code>git</code>和<code>root</code>两个。添加成功后<code>ssh -v git@HostIP</code>和<code>ssh -v root@HostIP</code>显示<code>Welcome to XXX !</code></p><h3 id="自动备份"><a href="#自动备份" class="headerlink" title="自动备份"></a>自动备份</h3><p>这里我按照「deploy的流程在服务器设置了自动化备份」<sup id="fnref:1" class="footnote-ref"><a href="#fn:1" rel="footnote"><span class="hint--top hint--rounded" aria-label="[Hexo博客部署到腾讯云服务器](https://www.muyiio.com/2020/03/28/hexo-bo-ke-bu-shu-dao-teng-xun-yun-fu-wu-qi/)">[1]</span></a></sup>，主要思路是在服务器设置一个独立的文件夹<code>backup</code>，再用类似<strong>deploy</strong>的钩子<code>blog.git</code>，构造一个备份的钩子<code>deploy.git</code>将博客的备份文件上传。</p><ul><li><p>获取<code>root</code>权限</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ su root<br></code></pre></td></tr></table></figure></li><li><p>建立<code>git</code>仓库</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ <span class="hljs-built_in">cd</span> /home/backup<br>$ git init --bare backup.git<br></code></pre></td></tr></table></figure></li><li><p>修改<code>backup.git</code>权限</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ chown git:git -R backup.git<br></code></pre></td></tr></table></figure></li><li><p>在 <code>/home/hexo/backup.git</code> 下，有一个自动生成的 <code>hooks</code> 文件夹，我们创建一个新的 <code>git</code> 钩子 <code>post-receive</code>，用于自动部署。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ vim backup.git/hooks/post-receive<br></code></pre></td></tr></table></figure></li><li><p>按 <code>i</code> 键进入文件的编辑模式，在该文件中添加两行代码（将下边的代码粘贴进去)，指定 Git 的工作树（源代码）和 Git 目录</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-meta">#!/bin/bash </span><br>git --work-tree=/home/backup --git-dir=/home/git/backup.git remote add origin<br>git --work-tree=/home/backup --git-dir=/home/git/backup.git checkout -f<br><span class="hljs-comment"># git --work-tree=/home/backup --git-dir=/home/git/backup.git checkout -b master # 创建切换分支</span><br>git --work-tree=/home/backup --git-dir=/home/git/backup.git push origin master <span class="hljs-comment"># 提交代码至分支</span><br></code></pre></td></tr></table></figure></li><li><p>按 <code>Esc</code> 键退出编辑模式，输入<code>:wq</code> 保存退出。（先输入<code>：</code>，然后输入<code>wq</code>回车）</p></li><li><p>修改文件权限，使得其可执行。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ chmod +x /home/git/backup.git/hooks/post-receive<br></code></pre></td></tr></table></figure></li><li><p>博客根目录<code>_config</code>下增加(因为服务器没有分支，默认是<code>master</code>，使用<code>backup</code>钩子)</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">deploy:</span><br>    <span class="hljs-attr">type:</span> <span class="hljs-string">git</span><br>    <span class="hljs-attr">repo:</span> <span class="hljs-string">root@***(服务器ip,内网外网都行):/home/git/backup.git</span>    <span class="hljs-comment">#仓库地址</span><br>    <span class="hljs-attr">branch:</span> <span class="hljs-string">master</span>    <span class="hljs-comment">#分支</span><br></code></pre></td></tr></table></figure></li><li><p>备份<code>hexo backup</code>(使用 <code>Hexo-Git-Backup</code> 插件)</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ hexo clean<br>$ hexo g<br>$ hexo b<br></code></pre></td></tr></table></figure></li></ul><p class="note note-primary">这个地方走了不少弯路，因为backup阶段每次都提示错误：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs vim">fata<span class="hljs-variable">l:</span> <span class="hljs-string">&#x27;xxx&#x27;</span> does not appear <span class="hljs-keyword">to</span> <span class="hljs-keyword">be</span> <span class="hljs-keyword">a</span> git repository<br>fata<span class="hljs-variable">l:</span> Could not <span class="hljs-keyword">read</span> from remote repository.<br></code></pre></td></tr></table></figure><p>我通过搜索<code>fatal-does-not-appear-to-be-a-git-repository</code>找到解决思路，用<code>Git命令</code>自动备份。详细参见「HEXO博客实现自动备份」<sup id="fnref:3" class="footnote-ref"><a href="#fn:3" rel="footnote"><span class="hint--top hint--rounded" aria-label="[HEXO博客实现自动备份](https://cwyaml.github.io/2017/03/07/backup/)">[3]</span></a></sup>。</p><ul><li><p>安装 <code>shelljs</code> 模块</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ npm install --save shelljs<br></code></pre></td></tr></table></figure></li><li><p>编写自动备份脚本：主题目录下<code>scripts</code>文件夹下新建一个<code>js</code>文件，文件名随意取，例如<code>update.js</code>。</p></li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;shelljs/global&#x27;</span>);<br><span class="hljs-keyword">try</span> &#123;<br>    <span class="hljs-comment">// hexo.on(&#x27;deployAfter&#x27;, function() &#123;         //当deploy完成后执行备份</span><br>    hexo.on(<span class="hljs-string">&#x27;backupAfter&#x27;</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;            <span class="hljs-comment">//当backup完成后执行备份</span><br>        run();<br>    &#125;);<br>&#125; <span class="hljs-keyword">catch</span> (e) &#123;<br>    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;产生了一个错误&lt;(￣3￣)&gt; !，错误详情为：&quot;</span> + e.toString());<br>&#125;<br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">run</span>(<span class="hljs-params"></span>) </span>&#123;<br>    <span class="hljs-keyword">if</span> (!which(<span class="hljs-string">&#x27;git&#x27;</span>)) &#123;<br>        echo(<span class="hljs-string">&#x27;Sorry, this script requires git&#x27;</span>);<br>        exit(<span class="hljs-number">1</span>);<br>    &#125; <span class="hljs-keyword">else</span> &#123;<br>        echo(<span class="hljs-string">&quot;======================Auto Backup Begin===========================&quot;</span>);<br>        cd(<span class="hljs-string">&#x27;XXXXXXX&#x27;</span>);    <span class="hljs-comment">//此处修改为Hexo根目录路径</span><br>        <span class="hljs-keyword">if</span> (exec(<span class="hljs-string">&#x27;git add .&#x27;</span>).code !== <span class="hljs-number">0</span>) &#123;<br>            echo(<span class="hljs-string">&#x27;Error: Git add failed&#x27;</span>);<br>            exit(<span class="hljs-number">1</span>);<br>        &#125;<br>        <span class="hljs-keyword">if</span> (exec(<span class="hljs-string">&#x27;git commit -m &quot;Form auto backup script\&#x27;s commit&quot;&#x27;</span>).code !== <span class="hljs-number">0</span>) &#123;<br>            <span class="hljs-keyword">if</span> (exec(<span class="hljs-string">&#x27;git remote add origin your_user_name@HostIP:/home/git/backup.git&#x27;</span>).code !== <span class="hljs-number">0</span>)&#123;       <span class="hljs-comment">//修改访问服务器方式</span><br>                <span class="hljs-keyword">if</span> (exec(<span class="hljs-string">&#x27;git push origin master&#x27;</span>).code !== <span class="hljs-number">0</span>) &#123;<br>                    echo(<span class="hljs-string">&#x27;Error: Git push failed&#x27;</span>);<br>                    exit(<span class="hljs-number">1</span>);<br>                &#125;<br>            &#125; <span class="hljs-keyword">else</span> &#123;<br>                echo(<span class="hljs-string">&#x27;Error: Git commit failed&#x27;</span>);<br>                exit(<span class="hljs-number">1</span>);<br>            &#125;<br>        &#125;<br>        <span class="hljs-keyword">if</span> (exec(<span class="hljs-string">&#x27;git push origin master&#x27;</span>).code !== <span class="hljs-number">0</span>) &#123;<br>            echo(<span class="hljs-string">&#x27;Error: Git push failed&#x27;</span>);<br>            exit(<span class="hljs-number">1</span>);<br>        &#125;<br>        echo(<span class="hljs-string">&quot;==================Auto Backup Complete============================&quot;</span>)<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>注意：</p><ul><li>在<code>Hexo根目录</code>或在主题目录下的<code>scripts</code>文件夹<code>js</code>在启动时就会自动载入，因此建议放在主题目录下，避免不必要的问题，例如<code>Blog/themes/fluid/scripts/update.js</code>。</li><li>此<code>backup.js</code>是在<code>hexo backup</code>运行后<code>backupAfter</code>自动触发的，因此可以在其他仓库备份(如<code>github</code>和<code>coding</code>)后实现服务器的自动备份。</li><li><code>git</code>远程仓库在服务器，所以push在服务器端的<code>home/git/backup.git</code>里<code>post-receive</code>钩子中。</li><li>由于设定了<code>git</code>免密设置，因此使用<code>backup.git</code>操作不需要重复的输入密码。</li></ul><p>在<code>_config.yml</code>下设置备份配置。<strong>注意：<code>backup</code>下不要有服务器的<code>repo</code></strong>，因为在<code>scripts</code>下<code>backup.js</code>已实现<code>git push</code>。</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">backup:</span><br>    <span class="hljs-attr">type:</span> <span class="hljs-string">git</span><br>    <span class="hljs-attr">message:</span> <span class="hljs-string">&#x27;backup updata: <span class="hljs-template-variable">&#123;&#123;now(&quot;YYYY-MM-DD HH/mm/ss&quot;)&#125;&#125;</span>&#x27;</span><br>    <span class="hljs-comment">## theme: fluid #主题</span><br>    <span class="hljs-attr">repo:</span> <span class="hljs-comment">#XXX为用户地址或IP地址</span><br>        <span class="hljs-attr">github:</span> <span class="hljs-string">git@github.com:XXX.github.io.git,backup</span><br>        <span class="hljs-attr">coding:</span> <span class="hljs-string">git@e.coding.net:XXX.git,backup</span><br>        <span class="hljs-comment">## hexo: root@<span class="hljs-doctag">XXX:</span>/home/git/backup.git,master</span><br></code></pre></td></tr></table></figure><p>再来一波四连😄</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ hexo clean <br>$ hexo g <br>$ hexo d <br>$ hexo b<br></code></pre></td></tr></table></figure><p>得到</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ hexo b<br>INFO  Start backup: git<br>The file will have its original line endings <span class="hljs-keyword">in</span> your working directory<br>[master 79b18cc] backup updata: XXXX<br> 2 files changed, 143 insertions(+), 4 deletions(-)<br>Branch <span class="hljs-string">&#x27;master&#x27;</span> <span class="hljs-built_in">set</span> up to track remote branch <span class="hljs-string">&#x27;backup&#x27;</span> from <span class="hljs-string">&#x27;github&#x27;</span>.<br>To github.com:XXXXXX<br>   89e022d..79b18cc  master -&gt; backup<br>Branch <span class="hljs-string">&#x27;master&#x27;</span> <span class="hljs-built_in">set</span> up to track remote branch <span class="hljs-string">&#x27;backup&#x27;</span> from <span class="hljs-string">&#x27;coding&#x27;</span>.<br>To e.coding.net:XXXXXX<br>   89e022d..79b18cc  master -&gt; backup<br>INFO  Backup <span class="hljs-keyword">done</span>: git<br>======================Auto Backup Begin===========================<br>On branch master<br>Your branch is up to date with <span class="hljs-string">&#x27;coding/backup&#x27;</span>.<br><br>nothing to commit, working tree clean<br>fatal: remote origin already exists.<br>remote: usage: git remote add [&lt;options&gt;] &lt;name&gt; &lt;url&gt;<br>remote:<br>remote:     -f, --fetch           fetch the remote branches<br>remote:     --tags                import all tags and associated objects when fetching<br>remote:                           or <span class="hljs-keyword">do</span> not fetch any tag at all (--no-tags)    <br>remote:     -t, --track &lt;branch&gt;  branch(es) to track<br>remote:     -m, --master &lt;branch&gt;<br>remote:                           master branch<br>remote:     --mirror[=(push|fetch)]<br>remote:                           <span class="hljs-built_in">set</span> up remote as a mirror to push to or fetch from<br>remote:<br>remote: fatal: <span class="hljs-string">&#x27;origin&#x27;</span> does not appear to be a git repository<br>remote: fatal: Could not <span class="hljs-built_in">read</span> from remote repository.<br>remote:<br>remote: Please make sure you have the correct access rights<br>remote: and the repository exists.<br>remote: fatal: not a git repository (or any of the parent directories): .git    <br>To XXXXXX:/home/git/backup.git<br>   89e022d..79b18cc  master -&gt; master<br>Everything up-to-date<br>==================Auto Backup Complete============================<br></code></pre></td></tr></table></figure><p>服务器的文件夹<code>/home/backup</code>下可看到<code>Hexo backup</code>的备份文件，服务器端备份的文件与<code>github</code>或<code>coding</code>备份文件一致。同时，文件夹<code>/home/hexo</code>是<code>hexo deploy</code>的部署文件，通过<code>Nginx</code>提供 <code>Web 服务</code>。这就实现了服务器部署和备份的自动化操作。</p><div class="note note-primary">            <p><strong>Tips</strong>:如果遇到程序没有报错但文件夹上传失败时，可以手动删除<code>Hexo</code>根目录下的<code>.deploy_git</code> 文件夹，再重新部署和备份。</p>           </div><h2 id="访问Jupyter"><a href="#访问Jupyter" class="headerlink" title="访问Jupyter"></a>访问Jupyter</h2><p>下面以域名<code>pxxyyz.com</code>为例，首先需要在服务器开启<code>Jupyter</code><sup id="fnref:4" class="footnote-ref"><a href="#fn:4" rel="footnote"><span class="hint--top hint--rounded" aria-label="[搭建 ipython/jupyter notebook 服务器](https://bitmingw.com/2017/07/09/run-jupyter-notebook-server/)">[4]</span></a></sup></p><h3 id="安装Jupyter"><a href="#安装Jupyter" class="headerlink" title="安装Jupyter"></a>安装Jupyter</h3><ul><li>安装<code>Anaconda</code></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ mkdir anaconda //创建目录<br>$ <span class="hljs-built_in">cd</span> anaconda    //进入目录<br>// 在镜像站找到安装包并下载<br>$ wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/Anaconda3-2020.02-Linux-x86_64.sh<br>$ bash Anaconda3-2020.02-Linux-x86_64.sh  //安装anaconda3<br></code></pre></td></tr></table></figure><ul><li>生成<code>Jupyter Notebook</code>配置文件</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ jupyter notebook --generate-config<br></code></pre></td></tr></table></figure><ul><li>打开<code>ipython</code>并设置登入密码</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs python">ipython <br>In [<span class="hljs-number">1</span>]: <span class="hljs-keyword">from</span> IPython.lib <span class="hljs-keyword">import</span> passwd<br>In [<span class="hljs-number">2</span>]: passwd()                         <span class="hljs-comment">#设置Jupyter Notebook密码</span><br>Enter password: <br>Verify password: <br>Out[<span class="hljs-number">2</span>]: <span class="hljs-string">&#x27;&#x27;</span>                               <span class="hljs-comment">#生成的密钥在配置文件有用</span><br></code></pre></td></tr></table></figure><ul><li>修改服务器配置文件</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ vim ~/.jupyter/jupyter_notebook_config.py<br></code></pre></td></tr></table></figure><ul><li>按 <code>i</code> 键进入文件的编辑模式，在该文件中添加代码，按 <code>Esc</code> 键退出编辑模式，输入<code>:wq</code> 保存退出。（先输入<code>:</code>，然后输入<code>wq</code>回车）</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 设置所有ip地址皆可访问</span><br>c.NotebookApp.ip=<span class="hljs-string">&#x27;*&#x27;</span> <br><span class="hljs-comment"># 输入密码的哈希值，见Out[2]</span><br>c.NotebookApp.password = u<span class="hljs-string">&#x27;sha1:XXX&#x27;</span><br><span class="hljs-comment"># 禁止自动打开浏览器</span><br>c.NotebookApp.open_browser = False  <br><span class="hljs-comment"># 指定端口，需要在服务器安全组开发该端口</span><br>c.NotebookApp.port =8888 <br><span class="hljs-comment"># 远程访问</span><br>c.NotebookApp.allow_remote_access = True <br><span class="hljs-comment"># 使用mathjax，可输入公式</span><br>c.NotebookApp.enable_mathjax = True<br></code></pre></td></tr></table></figure><ul><li>启动<code>Jupyter</code></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ nohup jupyter notebook --allow-root &amp; <br><span class="hljs-comment"># nohup避免关闭终端则终止了Jupyter 程序的运行，--allow-root允许root权限，&amp; 将程序放入后台运行</span><br></code></pre></td></tr></table></figure><ul><li>打开端口步骤： 本实例安全组-&gt;配置规则-&gt;入方向-&gt;手动添加</li></ul><center>| 授权策略 | 优先级 |  协议类型  |    端口范围    |   授权对象   || :------: | :----: | :--------: | :------------: | :----------: ||   允许   |  100   | 自定义 TCP | 目的:8888/8888 | 源:0.0.0.0/0 |</center><ul><li>浏览器访问<code>Jupyter notebook</code>(移动端或桌面端)，并输入刚才配置的密码即可使用</li></ul><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs html">http://HostIP:8888<br>http://域名:8888<br></code></pre></td></tr></table></figure><p><strong>注意</strong>：</p><ol><li>第二种方式需要域名解析到服务器公网IP，域名等价于公网IP</li><li><code>https://HostIP:8888</code>访问出错，<code>https://域名:8888</code>同理</li></ol><ul><li>anaconda换源，分别测试一下下载速度和稳定性，自行选择最优的</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 添加清华源</span><br>$ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/<br>$ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/<br>$ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge <br>$ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/<br>$ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/<br><br><span class="hljs-comment"># 添加上交源</span><br>$ conda config --add channels https://mirrors.sjtug.sjtu.edu.cn/anaconda/pkgs/main/ <br>$ conda config --add channels https://mirrors.sjtug.sjtu.edu.cn/anaconda/pkgs/free/<br>$ conda config --add channels https://mirrors.sjtug.sjtu.edu.cn/anaconda/cloud/conda-forge/ <br><br><span class="hljs-comment"># 添加中科大源</span><br>$ conda config --add channels https://mirrors.ustc.edu.cn/anaconda/pkgs/main/<br>$ conda config --add channels https://mirrors.ustc.edu.cn/anaconda/pkgs/free/<br>$ conda config --add channels https://mirrors.ustc.edu.cn/anaconda/cloud/conda-forge/<br>$ conda config --add channels https://mirrors.ustc.edu.cn/anaconda/cloud/msys2/<br>$ conda config --add channels https://mirrors.ustc.edu.cn/anaconda/cloud/bioconda/<br>$ conda config --add channels https://mirrors.ustc.edu.cn/anaconda/cloud/menpo/<br><br><span class="hljs-comment"># 设置搜索时显示通道地址 有资源显示源地址</span><br>$ conda config --<span class="hljs-built_in">set</span> show_channel_urls yes<br><br><span class="hljs-comment"># 换回默认源(重置用)</span><br>$ conda config --remove-key channels<br></code></pre></td></tr></table></figure><ul><li>可以在<code>Jupyter notebook</code>的工作目录中上传或下载<code>.ipynb</code>文件，当然别的文件也可以。</li></ul><h3 id="Nginx-重定向"><a href="#Nginx-重定向" class="headerlink" title="Nginx 重定向"></a>Nginx 重定向</h3><p>希望在我的博客中添加一个菜单按钮直接访问我的<code>Jupyter notebook</code>。然而菜单链接是通过<code>url_for</code>自动生成的，如添加<code>link: &#39;:8888&#39;</code>，生成的是<code>pxxyyz.com/:8888/</code>，而不是<code>pxxyyz.com:8888</code>。当然，可以设置链接为<code>link: &#39;pxxyyz.com:8888&#39;</code>，直接且简单，但这不能学到一些有趣的东西！</p><div class="note note-primary">            <p>下面通过特点子页面来访问域名的指定端口，即通过<code>pxxyyz.com/jupyter</code>访问<code>pxxyyz.com:8888</code></p>           </div><ul><li>在此之前上传了<code>SSL证书</code>并配置<code>HTTPS</code><sup id="fnref:5" class="footnote-ref"><a href="#fn:5" rel="footnote"><span class="hint--top hint--rounded" aria-label="[hexo部署服务器之配置HTTPS](https://www.kylin.show/25251.html)">[5]</span></a></sup></li><li>修改服务器配置文件</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ vim /etc/nginx/nginx.conf<br></code></pre></td></tr></table></figure><ul><li>按 <code>i</code> 键进入文件的编辑模式，在该文件找到<code>server</code>，修改代码，按 <code>Esc</code> 键退出编辑模式，输入<code>:wq</code> 保存退出。（先输入<code>:</code>，然后输入<code>wq</code>回车）</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs bash">  server &#123;<br>      listen       443 ssl;<span class="hljs-comment"># 80 default_server;</span><br>      <span class="hljs-comment"># listen       [::]:80 default_server;</span><br>      server_name  pxxyyz.com;<br>      root         /home/hexo;<br>      <span class="hljs-comment"># ssl on; # 老版本指令</span><br>      ssl_certificate /etc/nginx/conf/XXXXbundle.crt;<br>      ssl_certificate_key /etc/nginx/conf/XXXX.key;<br>      ssl_session_timeout 5m;<br>      ssl_protocols TLSv1 TLSv1.1 TLSv1.2;<br>      ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4!DHE;<br>      ssl_prefer_server_ciphers on;<br><br>      <span class="hljs-comment"># Load configuration files for the default server block.</span><br>      include /etc/nginx/default.d/*.conf;<br><br>      location / &#123;<br>      &#125;<br><br><span class="hljs-comment"># http://pxxyyz.com/jupyter/ to http://pxxyyz.com:8888</span><br>      location ~ /jupyter/?$ &#123;<br>        <span class="hljs-built_in">return</span> 302 http://pxxyyz.com:8888;<br>      &#125;<br>      <br>      error_page 404 /404.html;<br>          location = /40x.html &#123;<br>      &#125;<br><br>      error_page 500 502 503 504 /50x.html;<br>          location = /50x.html &#123;<br>      &#125;<br>  &#125;<br>  server &#123; <span class="hljs-comment">#把http的域名请求转成https</span><br>      listen 80;<br>      server_name pxxyyz.com;<br>      <span class="hljs-built_in">return</span> 301 https://$host<span class="hljs-variable">$request_uri</span>;<br>  &#125;<br></code></pre></td></tr></table></figure><ul><li>重启<code>nginx</code> 并检查配置</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ service nginx restart<br><span class="hljs-comment"># systemctl restart nginx.service</span><br>$ nginx -t<br><br><span class="hljs-comment"># 得到结果是</span><br><span class="hljs-comment"># nginx: the configuration file /etc/nginx/nginx.conf syntax is ok</span><br><span class="hljs-comment"># nginx: configuration file /etc/nginx/nginx.conf test is successful</span><br></code></pre></td></tr></table></figure><ul><li>当然，一开始不是这么做的，错误的方法也贴出来，避免入坑</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs bash">server &#123;<br>    listen 80;<br>    server_name pxxyyz.com;<br>    location /jupyter &#123;<br>          proxy_pass http://pxxyyz.com:8888/tree?<br>     &#125;<br>  &#125;<br></code></pre></td></tr></table></figure><ul><li><p>分析</p><ul><li><p>用<code>proxy_pass</code>得到的重定向是<code>https://pxxyyz.com/jupyter</code></p></li><li><p>对应的服务器访问的是<code>https://HostIP:8888</code></p></li><li><p>正确的<code>Jupyter notebook</code>访问地址却是<code>http://HostIP:8888</code></p></li><li><p>因此问题出在<strong>http的域名强制转成https</strong></p></li><li><p>解决方法：遇到指定链接用<code>return 302</code>返回<strong>http</strong>从而得到正确的结果<sup id="fnref:6" class="footnote-ref"><a href="#fn:6" rel="footnote"><span class="hint--top hint--rounded" aria-label="[NGINX rewrite location to another port](https://stackoverflow.com/questions/50734724/nginx-rewrite-location-to-another-port)">[6]</span></a></sup></p></li><li><p>结果：</p><ul><li><code>https://pxxyyz.com/jupyter</code>、<code>http://pxxyyz.com/jupyter</code>、<code>http://pxxyyz.com:8888</code>和<code>http://HostIP:8888</code>均能打开<code>Jupyter notebook</code></li><li>但<strong>IP的SSL证书不免费</strong>！<code>https+port</code>的组合访问会出错。</li></ul></li></ul></li></ul><h2 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h2><h3 id="数学公式"><a href="#数学公式" class="headerlink" title="数学公式"></a>数学公式</h3><p>在此补充一下之前公式不显示的问题。虽然<a href="https://hexo.fluid-dev.com/docs/">Fluid</a>主题支持<strong>LaTeX 数学公式</strong>，但是需要手动操作，而且我按照<a href="https://hexo.fluid-dev.com/docs/guide/#latex-%E6%95%B0%E5%AD%A6%E5%85%AC%E5%BC%8F">教程</a>开启本功能<code>mathjax</code>没有成功，即公式在网页里并没有被渲染和转换。通过网上查找，发现解决这类问题的思路主要是换渲染引擎<sup id="fnref:7" class="footnote-ref"><a href="#fn:7" rel="footnote"><span class="hint--top hint--rounded" aria-label="[如何在 hexo 中支持 Mathjax？](https://blog.csdn.net/u014630987/article/details/78670258)">[7]</span></a></sup>，例如<code>pandoc</code>、<code>mathjax</code>、<code>katex</code>。我目前使用<code>mathjax</code>，操作如下：</p><ul><li><p><strong>卸载</strong>默认引擎，并<strong>安装</strong>这个新的渲染引擎</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ npm uninstall hexo-renderer-marked --save <br>$ npm install hexo-renderer-kramed --save<br></code></pre></td></tr></table></figure></li><li><p>修改<code>/node_modules/hexo-renderer-kramed/lib/renderer.js</code></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// Change inline math rule</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">formatText</span>(<span class="hljs-params">text</span>) </span>&#123;<br><span class="hljs-comment">// Fit kramed&#x27;s rule: $$ + \1 + $$</span><br><span class="hljs-comment">// 直接返回text</span><br><span class="hljs-comment">// return text.replace(/`\$(.*?)\$`/g, &#x27;$$$$$1$$$$&#x27;);</span><br><span class="hljs-keyword">return</span> text;<br>&#125;<br></code></pre></td></tr></table></figure></li><li><p>修改hexo的渲染源码<code>/node_modules/kramed/lib/rules/inline.js</code></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// 去掉`\\`的额外转义，第11行，将其修改为</span><br><span class="hljs-comment">// escape: /^\\([\\`*&#123;&#125;\[\]()# +\-.!_&gt;])/, </span><br><span class="hljs-attr">escape</span>: <span class="hljs-regexp">/^\\([`*&#123;&#125;\[\]()# +\-.!_&gt;])/</span>,<br><span class="hljs-comment">// 将em标签对应的符号中，去掉`_`，第20行，将其修改为</span><br><span class="hljs-comment">// em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,    </span><br>em: <span class="hljs-regexp">/^\*((?:\*\*|[\s\S])+?)\*(?!\*)/</span>,<br></code></pre></td></tr></table></figure></li><li><p>停止使用 <code>hexo-math</code>，安装 <code>hexo-renderer-mathjax</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ npm uninstall hexo-math --save<br>// 不知道是不是必要的<br>$ npm install hexo-renderer-mathjax --save<br></code></pre></td></tr></table></figure></li><li><p>更新 <code>Mathjax</code> 的 <code>CDN</code> 链接，打开<code>/node_modules/hexo-renderer-mathjax/mathjax.html</code>，把<code>script</code>更改为：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs html">// <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_CHTML&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span> <br>// 网上推荐的上面这个，但我使用失败了，推荐下面这个，亲测可行<br><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br></code></pre></td></tr></table></figure></li><li><p>按照<a href="https://hexo.fluid-dev.com/docs/">Fluid</a>的<a href="https://hexo.fluid-dev.com/docs/guide/#%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B">快速开始</a>，需要修改<strong>主题配置</strong>，打开<code>/source/_data/fluid_config.yml</code> 文件</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">post:</span><br>  <span class="hljs-attr">math:</span>  <br>    <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span>  <br>    <span class="hljs-attr">specific:</span> <span class="hljs-literal">false</span>   <br>    <span class="hljs-attr">engine:</span> <span class="hljs-string">mathjax</span><br></code></pre></td></tr></table></figure></li><li><p>在根目录下修改<code>_config.yml</code>，添加</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">mathjax:</span> <span class="hljs-literal">true</span><br></code></pre></td></tr></table></figure></li><li><p>在<code>Front-matter</code>中打开<code>MathJax</code></p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-meta">---</span><br>  <span class="hljs-attr">mathjax:</span> <span class="hljs-literal">true</span><br><span class="hljs-meta">---</span><br></code></pre></td></tr></table></figure></li><li><p>显示数学公式</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs latex"># 不空行会出bug<br>$$\Sigma(&#123;n&#125; ; &#123;p&#125;)=\left\&#123;\left(\zeta_&#123;1&#125;, \ldots, \zeta_&#123;r&#125;\right) \in \mathbb&#123;C&#125;^&#123;n_&#123;1&#125;&#125; \times \cdots \times \mathbb&#123;C&#125;^&#123;n_&#123;r&#125;&#125;: \sum_&#123;k=1&#125;^&#123;r&#125;\left\|&#123;\zeta&#125;_&#123;k&#125;\right\|^&#123;2 p_&#123;k&#125;&#125; &lt; 1\right\&#125;$$<br></code></pre></td></tr></table></figure></li></ul><script type="math/tex; mode=display">\Sigma({n} ; {p})=\left\{\left(\zeta_{1}, \ldots, \zeta_{r}\right) \in \mathbb{C}^{n_{1}} \times \cdots \times \mathbb{C}^{n_{r}}: \sum_{k=1}^{r}\left\|{\zeta}_{k}\right\|^{2 p_{k}} < 1\right\}</script><div class="note note-primary">            <p>因为<code>hexo-renderer-kramed</code> 和<code>hexo-renderer-marked</code>均不支持<code>emoji</code> 功能，使用:smile: :blush: :smiley: :smirk:在Typora可以正常显示表情，在网页上显示的是<code>:smile: :blush: :smiley: :smirk:</code>，因此，可以直接复制<code>emoji</code>表情😄😊😃😏。</p>           </div><p>经过<strong><a href="https://zkqiang.cn/">强哥</a></strong>提醒，需要额外使用插件<code>hexo-filter-github-emojis</code>来支持<code>hexo</code> 的 <code>emoji</code> 。</p>  <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ npm install hexo-filter-github-emojis --save<br></code></pre></td></tr></table></figure><p>在根目录下<code>_config.yml</code>添加配置</p>  <figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">githubEmojis:</span><br>  <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br>  <span class="hljs-attr">className:</span> <span class="hljs-string">github-emoji</span><br>  <span class="hljs-attr">inject:</span> <span class="hljs-literal">true</span><br>  <span class="hljs-attr">styles:</span><br>  <span class="hljs-attr">customEmojis:</span><br></code></pre></td></tr></table></figure><p>正文<code>markdown</code>用如下格式使用 <code>emoji</code></p> <figure class="highlight django"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs django"><span class="xml"># 不空行会出bug</span><br><span class="xml"></span><span class="hljs-template-tag">&#123;% <span class="hljs-name">raw</span> %&#125;</span><span class="hljs-template-tag">&#123;% <span class="hljs-name">github_emoji</span> emoji %&#125;</span><span class="hljs-template-tag">&#123;% <span class="hljs-name">endraw</span> %&#125;</span><br></code></pre></td></tr></table></figure><p>正如上面所说，Hexo 默认情况下 <code>:emoji:</code> 不能正确显示表情，如<code>:tada:</code>，而该插件通过 <code>{%github_emoji tada%}(github_emoji tada)</code> 即可显示这个 emoji ，其他调用格式可以看 <code>hexo-filter-github-emojis</code> 的<a href="https://github.com/crimx/hexo-filter-github-emojis">官方文档</a>。</p><h3 id="一键三连"><a href="#一键三连" class="headerlink" title="一键三连"></a>一键三连</h3><p>前面提到的在根目录下使用<code>Git Bash Here</code>输入下面指令有些繁琐。</p>  <figure class="highlight sas"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sas">$ hexo clean <span class="hljs-variable">&amp;&amp;</span> hexo g <span class="hljs-variable">&amp;&amp;</span> hexo d <span class="hljs-variable">&amp;&amp;</span> hexo b<br></code></pre></td></tr></table></figure><p>现在用<code>.bat</code>文件简化，可以分别保存。其中<strong>第四行的地址为根目录</strong>。</p><figure class="highlight livescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs livescript">:: 一键预览<br>@echo <span class="hljs-literal">on</span><br>D:<br>cd D:<span class="hljs-string">\github\Hexo-Blog</span><br>hexo clean &amp;&amp; hexo g &amp;&amp; hexo s<br></code></pre></td></tr></table></figure><figure class="highlight livescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs livescript">:: 一键部署<br>@echo <span class="hljs-literal">on</span><br>D:<br>cd D:<span class="hljs-string">\github\Hexo-Blog</span><br>hexo clean &amp;&amp; hexo g &amp;&amp; hexo d<br></code></pre></td></tr></table></figure><figure class="highlight sas"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs sas">:: 一键部署+备份<br>@echo <span class="hljs-meta">on</span><br>D:<br>cd D:\github\Hexo-Blog<br>hexo clean <span class="hljs-variable">&amp;&amp;</span> hexo g <span class="hljs-variable">&amp;&amp;</span> hexo d <span class="hljs-variable">&amp;&amp;</span> hexo b<br></code></pre></td></tr></table></figure><p><strong>一键预览</strong>的窗口支持实时修改实时显示，即文档修改保存后，刷新可得修改后的预览页面。如果做到ssh免密登入，部署与备份也能一键完成。</p><div class="note note-danger" style="text-align:center;color:#0000FF;">免密会带来安全隐患，部署和备份文件最好设置为<div class="label label-default" style="color:#FF0000;font-size:1.2em;font-weight: bold;">私密</div></div><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1" class="footnote-text"><span><a href="https://www.muyiio.com/2020/03/28/hexo-bo-ke-bu-shu-dao-teng-xun-yun-fu-wu-qi/">Hexo博客部署到腾讯云服务器</a><a href="#fnref:1" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:2" class="footnote-text"><span><a href="https://liujunzhou.top/deployer/">使用Git+Hooks实现Hexo站点自动部署到CentOS服务器上</a><a href="#fnref:2" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:3" class="footnote-text"><span><a href="https://cwyaml.github.io/2017/03/07/backup/">HEXO博客实现自动备份</a><a href="#fnref:3" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:4" class="footnote-text"><span><a href="https://bitmingw.com/2017/07/09/run-jupyter-notebook-server/">搭建 ipython/jupyter notebook 服务器</a><a href="#fnref:4" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:5" class="footnote-text"><span><a href="https://www.kylin.show/25251.html">hexo部署服务器之配置HTTPS</a><a href="#fnref:5" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:6" class="footnote-text"><span><a href="https://stackoverflow.com/questions/50734724/nginx-rewrite-location-to-another-port">NGINX rewrite location to another port</a><a href="#fnref:6" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:7" class="footnote-text"><span><a href="https://blog.csdn.net/u014630987/article/details/78670258">如何在 hexo 中支持 Mathjax？</a><a href="#fnref:7" rev="footnote" class="footnote-backref"> ↩</a></span></span></li></ol></div></section>]]></content>
    
    
    <categories>
      
      <category>实用技巧</category>
      
    </categories>
    
    
    <tags>
      
      <tag>部署</tag>
      
      <tag>Hexo</tag>
      
      <tag>用户经验</tag>
      
      <tag>花里胡哨</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>常见静态网站托管平台使用及多节点部署方案</title>
    <link href="/blog/posts/9604e2c1.html"/>
    <url>/blog/posts/9604e2c1.html</url>
    
    <content type="html"><![CDATA[<div class="note note-success">            <p>本文由 Fluid 用户授权转载，版权归原作者所有。</p> <p>本文作者：Vince<br>原文地址：<a href="https://i.vince.pub/posts/2fc062cb/">https://i.vince.pub/posts/2fc062cb/</a></p>           </div><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>对于 Hexo 来说，我们使用它来部署博客是因为无后端运维和高速渲染页面等优点。选择一个合适的托管平台对于博客来说十分重要，可以免费使用且稳定高速的平台是不存在的，我们总是需要做出妥协。我使用了 Github Pages、Coding Pages、Gitee Pages、Netlify 和 Vercel 来部署博客，以下为我的使用报告。</p><h2 id="常见托管平台"><a href="#常见托管平台" class="headerlink" title="常见托管平台"></a>常见托管平台</h2><p><img src="https://cdn.vince.pub/blog-file/photo/2fc062cb2.svg" alt="节点"></p><h3 id="Github-Pages"><a href="#Github-Pages" class="headerlink" title="Github Pages"></a>Github Pages</h3><p><span class="label label-primary">免费</span><span class="label label-success">扩展性强</span><span class="label label-danger">无限制性</span></p><p>使用体验：可以与仓库无缝对接，高效部署，但是没用设置国内节点，在国内访问速度较慢，作为一个海外节点还是非常不错的。相对而言，使用 jsdelivr 来加速网站相关文件可以满足基本使用。查看 Github Status，Pages 服务会出现偶尔挂掉的情况，但多数仓库文档、演示等都选择了 Github Pages 服务。</p><p>使用及扩展：提供二级域名，支持域名绑定及免费 SSL 证书。网站内容与仓库保存一致，自动推送。通过使用 Github Actions 具有较强扩展性。</p><h3 id="Netlify"><a href="#Netlify" class="headerlink" title="Netlify"></a>Netlify</h3><p><span class="label label-primary">免费</span><span class="label label-success">扩展性强</span><span class="label label-danger">无限制性</span></p><p>使用体验：Netlify 的节点设置在海外，但 Netlify 的服务速度尚可，国内部分地区可以到达高速服务。在使用 CDN 的情况下，把网站部署在 Netlify 是可以比较好的选择。Vuejs 和 Hexo 的官网都部署在 Netlify 上，其稳定性可想而知。Netlify 虽然拥有付费功能，但是基本上我们需要使用到的服务都免费。</p><p>使用及扩展：提供二级域名，支持域名绑定及免费 SSL 证书。Netlify 支持 Github 或者 Gitlab 等账号登录，如果仓库已经是静态网站文件，每次 Push 到仓库 Netlify 都会自动部署。支持 Build Command，源文件也可以通过提供的环境自动编译或渲染，类似于一款 CI，与 Github Pages 功能相近。</p><h3 id="Vercel"><a href="#Vercel" class="headerlink" title="Vercel"></a>Vercel</h3><p><span class="label label-primary">免费</span><span class="label label-success">扩展性强</span><span class="label label-danger">无限制性</span></p><p>使用体验：Vercel 的体验情况总体和 Netlify 相近，节点设置在海外，访问速度尚可。前身是 now.sh，作为一个高质量的静态托管平台，Vercel的使用体验非常好，是一个可选的优秀平台。</p><p>使用及扩展：提供二级域名，支持域名绑定及免费 SSL 证书。支持 Github 或者 Gitlab 等账号登录，如果仓库已经是静态网站文件，每次 Push 到仓库都会自动部署。Vercel 打出了 free forever 的口号，也就是说在非商用的情况下，个人可以永久免费使用。支持设置环境并执行相关命令，自动部署不在话下。</p><h3 id="Coding"><a href="#Coding" class="headerlink" title="Coding"></a>Coding</h3><p><span class="label label-primary">免费</span><span class="label label-success">一般扩展性</span><span class="label label-danger">限制性</span></p><p>使用体验：Coding 是腾讯系的一个国内托管平台，对于人数较少的团体实行免费制度。服务器节点部署在国内，在国内使用访问速度较快。也是国内开放程度比较高的一个代码托管平台了，静态网站功能 Coding 最近改版了一下，相对于之前来说更稳定了一些。</p><p>使用及扩展：提供二级域名，支持域名绑定及免费 SSL 证书。基于 Kubernetes 的持续部署，可以人我们体验到与 DevOps 体系紧密结合的持续部署能力。持续中提供静态网站托管，但是静态网站托管需要实名和绑定手机号。</p><h3 id="Gitee"><a href="#Gitee" class="headerlink" title="Gitee"></a>Gitee</h3><p><span class="label label-primary">免费（国内限制）</span><span class="label label-success">扩展性较低</span><span class="label label-danger">限制性强</span></p><p>使用体验：Gitee 是一个国内托管平台，对比 coding 来说较为封闭。静态托管功能上拥有较大限制，且无法自动部署，功能残缺。</p><p>使用及扩展：提供二级域名，非付费版不支持自动部署、域名绑定及免费 SSL 证书。如果强制使用 https，可能会造成样式文件失效等问题。</p><h3 id="TCB"><a href="#TCB" class="headerlink" title="TCB"></a>TCB</h3><p><span class="label label-primary">付费</span><span class="label label-success">扩展性高</span><span class="label label-danger">一般限制性</span></p><p>使用体验：TCB(Tencent CloudBase）采用 serverless 架构，提供静态托管服务。我的主站就是使用 TCB，相对而言因为付费了，所以效果较好，在全国各地有 CDN 节点，目前使用是因为腾讯的赞助计划，如果赞助计划失效了，价格过高可能会考虑切换平台。空间较大，流量较多，已经充当 CDN 使用了。</p><p>使用及扩展：提供二级域名，支持自动部署及 免费SSL 证书，但是 SSL 证书申请可能需要备案。扩展性较强，可以使用 CLI 工具或者 Tencent CloudBase Github Action 来部署。</p><h2 id="多节点部署方案"><a href="#多节点部署方案" class="headerlink" title="多节点部署方案"></a>多节点部署方案</h2><p><img src="https://cdn.vince.pub/blog-file/photo/2fc062cb1.png" alt=""></p><h3 id="几个仓库"><a href="#几个仓库" class="headerlink" title="几个仓库"></a>几个仓库</h3><h4 id="Hexo-源码仓库"><a href="#Hexo-源码仓库" class="headerlink" title="Hexo 源码仓库"></a>Hexo 源码仓库</h4><p>从图中可以看到使用了 <code>Blog-Source</code> 这个仓库为 Hexo 源码仓库，这个仓库有一个使用了两个 Github Actions，一个用来渲染博客文件并推送到 TCB 静态托管平台，一个用来渲染博客文件推送到各个 Git 仓库，理论上一个 Action 也可以完成这些任务，但是便于管理我选择了两个 Action。</p><p>推送至各个 Git 仓库</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">Repo(Github,</span> <span class="hljs-string">Coding,</span> <span class="hljs-string">Gitee)</span><br><span class="hljs-attr">on:</span> [<span class="hljs-string">push</span>]<br><span class="hljs-attr">jobs:</span><br>  <span class="hljs-attr">build:</span><br>    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span><br>    <span class="hljs-attr">env:</span> <br>      <span class="hljs-attr">hTZ:</span> <span class="hljs-string">Asia/Shanghai</span><br>    <span class="hljs-attr">steps:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span><br>      <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span><br>      <span class="hljs-attr">with:</span><br>        <span class="hljs-attr">ref:</span> <span class="hljs-string">master</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Update</span> <span class="hljs-string">Submodule</span><br>      <span class="hljs-attr">run:</span> <span class="hljs-string">|</span><br><span class="hljs-string">        git submodule init</span><br><span class="hljs-string">        git submodule update --remote</span><br><span class="hljs-string"></span>    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Node</span><br>      <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v1</span><br>      <span class="hljs-attr">with:</span><br>        <span class="hljs-attr">node-version:</span> <span class="hljs-string">&quot;10.x&quot;</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Hexo</span> <span class="hljs-string">Generate</span><br>      <span class="hljs-attr">run:</span> <span class="hljs-string">|</span><br><span class="hljs-string">        rm -f .yarnclean</span><br><span class="hljs-string">        yarn --frozen-lockfile --ignore-engines --ignore-optional --non-interactive --silent --ignore-scripts --production=false</span><br><span class="hljs-string">        rm -rf ./public</span><br><span class="hljs-string">        yarn run hexo clean</span><br><span class="hljs-string">        yarn run hexo generate</span><br><span class="hljs-string"></span>    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Hexo</span> <span class="hljs-string">Deploy</span><br>      <span class="hljs-attr">env:</span><br>        <span class="hljs-attr">SSH_PRIVATE:</span> <span class="hljs-string">$&#123;&#123;</span> <span class="hljs-string">secrets.SSH_PRIVATE</span> <span class="hljs-string">&#125;&#125;</span><br>        <span class="hljs-attr">GIT_NAME:</span> <span class="hljs-string">vinceying</span><br>        <span class="hljs-attr">GIT_EMAIL:</span> <span class="hljs-string">admin@vicne.pub</span><br>      <span class="hljs-attr">run:</span> <span class="hljs-string">|</span><br><span class="hljs-string">        mkdir -p ~/.ssh/</span><br><span class="hljs-string">        echo &quot;$SSH_PRIVATE&quot; | tr -d &#x27;\r&#x27; &gt; ~/.ssh/id_rsa</span><br><span class="hljs-string">        chmod 600 ~/.ssh/id_rsa</span><br><span class="hljs-string">        ssh-keyscan e.coding.net &gt;&gt; ~/.ssh/known_hosts</span><br><span class="hljs-string">        ssh-keyscan github.com &gt;&gt; ~/.ssh/known_hosts</span><br><span class="hljs-string">        ssh-keyscan gitee.com &gt;&gt; ~/.ssh/known_hosts</span><br><span class="hljs-string">        git config --global user.name &quot;$GIT_NAME&quot;</span><br><span class="hljs-string">        git config --global user.email &quot;$GIT_EMAIL&quot;</span><br><span class="hljs-string">        yarn run hexo deploy</span><br></code></pre></td></tr></table></figure><p>推送至 TCB</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">Tencent</span> <span class="hljs-string">CloudBase</span><br><span class="hljs-attr">on:</span> <span class="hljs-string">push</span><br><span class="hljs-attr">jobs:</span><br>  <span class="hljs-attr">build:</span><br>    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span><br>    <span class="hljs-attr">env:</span><br>      <span class="hljs-attr">TZ:</span> <span class="hljs-string">Asia/Shanghai</span><br>    <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">Hexo</span> <span class="hljs-string">Souce</span> <span class="hljs-string">Repo</span> <span class="hljs-string">to</span> <span class="hljs-string">Tencent</span> <span class="hljs-string">CloudBase</span><br>    <span class="hljs-attr">steps:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span><br>        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Node</span><br>        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v1</span><br>        <span class="hljs-attr">with:</span><br>          <span class="hljs-attr">node-version:</span> <span class="hljs-string">&#x27;10.x&#x27;</span><br>      <span class="hljs-comment"># NPM 环境及 Hexo 部署</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">NPM</span><br>        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Clean</span><br>        <span class="hljs-attr">run:</span> <span class="hljs-string">./node_modules/.bin/hexo</span> <span class="hljs-string">clean</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Generate</span><br>        <span class="hljs-attr">run:</span> <span class="hljs-string">./node_modules/.bin/hexo</span> <span class="hljs-string">generate</span><br>      <span class="hljs-comment"># Deploy static to Tencent CloudBase</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">static</span> <span class="hljs-string">to</span> <span class="hljs-string">Tencent</span> <span class="hljs-string">CloudBase</span><br>        <span class="hljs-attr">id:</span> <span class="hljs-string">deployStatic</span><br>        <span class="hljs-attr">uses:</span> <span class="hljs-string">TencentCloudBase/cloudbase-action@v1.1.1</span><br>        <span class="hljs-attr">with:</span><br>          <span class="hljs-attr">secretId:</span> <span class="hljs-string">$&#123;&#123;</span> <span class="hljs-string">secrets.SECRET_ID</span> <span class="hljs-string">&#125;&#125;</span><br>          <span class="hljs-attr">secretKey:</span> <span class="hljs-string">$&#123;&#123;</span> <span class="hljs-string">secrets.SECRET_KEY</span> <span class="hljs-string">&#125;&#125;</span><br>          <span class="hljs-attr">envId:</span> <span class="hljs-string">$&#123;&#123;</span> <span class="hljs-string">secrets.ENV_ID</span> <span class="hljs-string">&#125;&#125;</span><br>          <span class="hljs-attr">staticSrcPath:</span> <span class="hljs-string">public</span><br></code></pre></td></tr></table></figure><h4 id="Github-博客页面仓库"><a href="#Github-博客页面仓库" class="headerlink" title="Github 博客页面仓库"></a>Github 博客页面仓库</h4><p>这个作为使用 Github Pages 服务的仓库，同时在 Netlify 和 Vercel 的选择为源仓库，在每次推送至本仓库时，Netlify 和 Vercel 都会自动部署新文件。</p><h4 id="CDN-文件仓库"><a href="#CDN-文件仓库" class="headerlink" title="CDN 文件仓库"></a>CDN 文件仓库</h4><p>这个仓库作为管理和存放一些需要推送到 CDN 的文件，比如 css 文件、图片和视频等，首先是为了便于管理及通过 Github Actions推送 到 TCB，其次是为了使用 Jsdelivr CDN 服务作为备用 CDN。</p><h3 id="方案优点"><a href="#方案优点" class="headerlink" title="方案优点"></a>方案优点</h3><ul><li>高效自动化，利用 Github Actions,每次只要 Push 到 <code>Blog-Souce</code>和<code>Blog-file</code>仓库就可以全仓库和全节点同步。</li><li>便于管理文件，当主 CDN 失效后，直接替换 CDN 地址链接即可完成启用备用 CDN，且备份了文件。</li><li>多设备管理，当切换设备后，直接在不安装环境的情况下直接 Clone 即可管理博客，但调试方面还是需要安装环境。特别是在 Github 的云端 IDE-Codespace 正式发布后，可以完全通过仓库管理博客。</li></ul>]]></content>
    
    
    <categories>
      
      <category>实用技巧</category>
      
    </categories>
    
    
    <tags>
      
      <tag>部署</tag>
      
      <tag>Hexo</tag>
      
      <tag>用户经验</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>centos7.7下安装postgresql12.3</title>
    <link href="/blog/posts/27e03ac5.html"/>
    <url>/blog/posts/27e03ac5.html</url>
    
    <content type="html"><![CDATA[<h2 id="安装依赖"><a href="#安装依赖" class="headerlink" title="安装依赖"></a>安装依赖</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">yum install -y readline-devel zlib-devel make gcc<br></code></pre></td></tr></table></figure><h2 id="创建用户组和用户、密码"><a href="#创建用户组和用户、密码" class="headerlink" title="创建用户组和用户、密码"></a>创建用户组和用户、密码</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs shell">groupadd -g 1000 postgres<br>useradd -g 1000 -u 1000 postgres<br>passwd postgres<br></code></pre></td></tr></table></figure><span id="more"></span><div class="danger">安装完才发现有更简单的方法！</div><p>打开<code>postgresql</code>的官网安装教程：<a href="https://www.postgresql.org/download/linux/redhat/">https://www.postgresql.org/download/linux/redhat/</a> ，选择一下系统</p><img src="/blog/posts/27e03ac5/image-20200619125645752.png" class="" title="image-20200619125645752"><p>就会发现安装如下的命令行：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs shell">yum install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm<br>yum install postgresql12-server<br>/usr/pgsql-12/bin/postgresql-12-setup initdb<br>systemctl enable postgresql-12<br>systemctl start postgresql-12<br></code></pre></td></tr></table></figure><h2 id="安装前准备"><a href="#安装前准备" class="headerlink" title="安装前准备"></a>安装前准备</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs shell">mkdir -p /opt/pg12/&#123;data,backup,scripts,archive_wals&#125;<br>chown -R postgres.postgres /opt/pg12/<br>chmod 0700 /opt/pg12/data<br></code></pre></td></tr></table></figure><h2 id="解压编译安装"><a href="#解压编译安装" class="headerlink" title="解压编译安装"></a>解压编译安装</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs shell">tar -zxvf postgresql-12.3.tar.gz <br>./configure  --prefix=/opt/pg12 --with-pgport=5432<br>gmake world<br>gmake install-world<br></code></pre></td></tr></table></figure><h2 id="配置环境变量"><a href="#配置环境变量" class="headerlink" title="配置环境变量"></a>配置环境变量</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs shell"><span class="hljs-meta">#</span><span class="bash"> 切换到postgres账户</span><br>su - postgres<br><span class="hljs-meta">#</span><span class="bash"> 编辑用户下配置文件</span><br>vim /home/postgres/.bashrc<br></code></pre></td></tr></table></figure><p>编辑内容如下:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs shell">PG_HOME=/opt/pg12/<br><br>LD_LIBRARY_PATH=$PG_HOME/lib:$LD_LIBRARY_PATH<br>PATH=$PG_HOME/bin:$PATH<br><br>PKG_CONFIG_PATH=$PG_HOME/lib/pkgconfig:$PKG_CONFIG_PATH<br>export PKG_CONFIG_PATH LD_LIBRARY_PATH<br></code></pre></td></tr></table></figure><p><code>source /etc/profile</code> 更新配置！</p><h2 id="初始化数据库"><a href="#初始化数据库" class="headerlink" title="初始化数据库"></a>初始化数据库</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs shell"><span class="hljs-meta">#</span><span class="bash">在postgres账户下执行</span><br>/opt/pg12/bin/initdb -D /opt/pg12/data/ -W<br>/opt/pg12/bin/pg_ctl -D /opt/pg12/data/ -l logfile start<br></code></pre></td></tr></table></figure><h2 id="启动数据库"><a href="#启动数据库" class="headerlink" title="启动数据库"></a>启动数据库</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">pg_ctl start -D $PGDATA<br></code></pre></td></tr></table></figure><h2 id="设置监听"><a href="#设置监听" class="headerlink" title="设置监听"></a>设置监听</h2><p>修改<code>postgres/data</code>目录下的<code>pg_hba.conf</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">vim $PGDATA/pg_hba.conf<br></code></pre></td></tr></table></figure><p>修改<code>IPv4</code>一行内容如下:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># IPv4 local connections:</span><br>host    all             all             0.0.0.0/0            trust<br></code></pre></td></tr></table></figure><p>修改<code>postgresql.conf</code>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">vim <span class="hljs-variable">$PGDATA</span>/postgresql.conf<br></code></pre></td></tr></table></figure><p>修改监听一节如下:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># - Connection Settings -</span><br>listen_addresses = <span class="hljs-string">&#x27;*&#x27;</span> <br>port = 5432 <br></code></pre></td></tr></table></figure><p><code>wq!</code>保存退出。<br>重启pg服务生效</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">pg_ctl restart -D <span class="hljs-variable">$PGDATA</span><br></code></pre></td></tr></table></figure><h2 id="设置开机自启动"><a href="#设置开机自启动" class="headerlink" title="设置开机自启动"></a>设置开机自启动</h2><p>创建 <code>postgresql.service</code> 服务脚本</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">vim /usr/lib/systemd/system/postgresql.service<br></code></pre></td></tr></table></figure><p>编辑内容如下:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs bash">[Unit]<br>Description=PostgreSQL database server<br>After=network.target<br><br>[Service]<br>Type=forking <span class="hljs-comment">#服务的类型，常用的有 simple（默认类型） 和 forking。默认的 simple 类型可以适应于绝大多数的场景，因此一般可以忽略这个参数的配置。而如果服务程序启动后会通过 fork 系统调用创建子进程，然后关闭应用程序本身进程的情况，则应该将 Type 的值设置为 forking，否则 systemd 将不会跟踪子进程的行为，而认为服务已经退出。 pg需要通过fork来创建一些子进程，所以这里选择forKing</span><br><br>User=postgres<br>Group=postgres<br><br><span class="hljs-comment"># Port number for server to listen on</span><br>Environment=PGPORT=5432<br><br><span class="hljs-comment"># Location of database directory</span><br>Environment=PGDATA=/opt/pg12/data<br><br><span class="hljs-comment"># Where to send early-startup messages from the server (before the logging</span><br><span class="hljs-comment"># options of postgresql.conf take effect)</span><br><span class="hljs-comment"># This is normally controlled by the global default set by systemd</span><br><span class="hljs-comment"># StandardOutput=syslog</span><br><br><span class="hljs-comment"># Disable OOM kill on the postmaster</span><br>OOMScoreAdjust=-1000<br><br><span class="hljs-comment">#ExecStartPre=/opt/pg12/bin/postgresql-check-db-dir $&#123;PGDATA&#125;</span><br>ExecStart=/opt/pg12/bin/pg_ctl start -D <span class="hljs-variable">$&#123;PGDATA&#125;</span> -s -o <span class="hljs-string">&quot;-p <span class="hljs-variable">$&#123;PGPORT&#125;</span>&quot;</span> -w -t 300<br>ExecStop=/opt/pg12/bin/pg_ctl stop -D <span class="hljs-variable">$&#123;PGDATA&#125;</span> -s -m fast<br>ExecReload=/opt/pg12/bin/pg_ctl reload -D <span class="hljs-variable">$&#123;PGDATA&#125;</span> -s<br><br><span class="hljs-comment"># Give a reasonable amount of time for the server to start up/shut down</span><br>TimeoutSec=300<br><br>[Install]<br>WantedBy=multi-user.target<br></code></pre></td></tr></table></figure><p>如果之前使用<code>pg_ctl restart -D $PGDATA</code>启动过，先停止<code>pg_ctl stop -D $PGDATA</code></p><p>更新设置:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">systemctl daemon-reload<br></code></pre></td></tr></table></figure><p>配置开机自启动:</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs routeros">systemctl <span class="hljs-builtin-name">enable</span> pgserver.service<br></code></pre></td></tr></table></figure><h2 id="常用指令"><a href="#常用指令" class="headerlink" title="常用指令"></a>常用指令</h2><p>启动服务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">systemctl  start  postgresql.service<br></code></pre></td></tr></table></figure><p>停止服务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">systemctl stop postgresql.service<br></code></pre></td></tr></table></figure><p>重启服务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">systemctl restart postgresql.service<br>service postgresql restart<br></code></pre></td></tr></table></figure><p>使服务自动启动</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">systemctl <span class="hljs-built_in">enable</span> postgresql.service<br></code></pre></td></tr></table></figure><p>使服务不自动启动</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">systemctl <span class="hljs-built_in">disable</span> postgresql.service<br></code></pre></td></tr></table></figure><p>检查服务状态</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">systemctl   status  postgresql.service<br>systemctl   is-active postgresql.service<br></code></pre></td></tr></table></figure><p>显示所有已启动的服务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">systemctl   list-units --<span class="hljs-built_in">type</span>=service<br></code></pre></td></tr></table></figure>]]></content>
    
    
    
    <tags>
      
      <tag>Postgres</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>利用 GitHub Actions 自动部署 Hexo 博客</title>
    <link href="/blog/posts/9604e2c9.html"/>
    <url>/blog/posts/9604e2c9.html</url>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文主要讲如何应用于 Hexo 部署中，如果还不太熟悉 GitHub Actions 可以看<a href="https://zkqiang.cn/posts/e8ed6836/">这篇文章</a>，简单地说 Actions 就是在设定的时机触发创建一个虚拟云环境，然后执行一连串动作，从而实现自动部署的功能。</p><h2 id="创建工作流"><a href="#创建工作流" class="headerlink" title="创建工作流"></a>创建工作流</h2><p>首先要保证你的 Hexo 博客项目是全部提交到 GitHub 仓库中，然后在博客目录下创建 <code>.github/workflows/xxx.yml</code> 文件，文件名任意。</p><p>文件内容如下，根据自己的需求增删 step：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span>                      <span class="hljs-comment"># Actions 显示的名字，随意设置</span><br><br><span class="hljs-attr">on:</span> [<span class="hljs-string">push</span>]                        <span class="hljs-comment"># 监听到 push 事件后触发</span><br><br><span class="hljs-attr">jobs:</span><br>  <span class="hljs-attr">build:</span><br><br>    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span><br><br>    <span class="hljs-attr">steps:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span>              <span class="hljs-comment"># 拉取当前执行 Actions 仓库的指定分支</span><br>      <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span><br>      <span class="hljs-attr">with:</span><br>        <span class="hljs-attr">ref:</span> <span class="hljs-string">master</span><br><br>    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Update</span> <span class="hljs-string">Submodule</span>      <span class="hljs-comment"># 如果仓库有 submodule，在这里更新，没有则删掉此步骤</span><br>      <span class="hljs-attr">run:</span> <span class="hljs-string">|</span><br><span class="hljs-string">        git submodule init</span><br><span class="hljs-string">        git submodule update --remote</span><br><span class="hljs-string"></span><br>    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Node</span>            <span class="hljs-comment"># 安装 Node 环境</span><br>      <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v1</span><br>      <span class="hljs-attr">with:</span><br>        <span class="hljs-attr">node-version:</span> <span class="hljs-string">&quot;10.x&quot;</span><br><br>    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Hexo</span> <span class="hljs-string">Generate</span>         <span class="hljs-comment"># 安装 Hexo 依赖并且生成静态文件</span><br>      <span class="hljs-attr">run:</span> <span class="hljs-string">|</span><br><span class="hljs-string">        rm -f .yarnclean</span><br><span class="hljs-string">        yarn --frozen-lockfile --ignore-engines --ignore-optional --non-interactive --silent --ignore-scripts --production=false</span><br><span class="hljs-string">        rm -rf ./public</span><br><span class="hljs-string">        yarn run hexo clean</span><br><span class="hljs-string">        yarn run hexo generate</span><br><span class="hljs-string"></span><br>    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Hexo</span> <span class="hljs-string">Deploy</span>           <span class="hljs-comment"># 部署步骤，这里以 hexo deploy 为例</span><br>      <span class="hljs-attr">env:</span><br>        <span class="hljs-attr">SSH_PRIVATE:</span> <span class="hljs-string">$&#123;&#123;</span> <span class="hljs-string">secrets.SSH_PRIVATE</span> <span class="hljs-string">&#125;&#125;</span><br>        <span class="hljs-attr">GIT_NAME:</span> <span class="hljs-string">yourname</span><br>        <span class="hljs-attr">GIT_EMAIL:</span> <span class="hljs-string">your@email.com</span><br>      <span class="hljs-attr">run:</span> <span class="hljs-string">|</span><br><span class="hljs-string">        mkdir -p ~/.ssh/</span><br><span class="hljs-string">        echo &quot;$SSH_PRIVATE&quot; | tr -d &#x27;\r&#x27; &gt; ~/.ssh/id_rsa</span><br><span class="hljs-string">        chmod 600 ~/.ssh/id_rsa</span><br><span class="hljs-string">        ssh-keyscan github.com &gt;&gt; ~/.ssh/known_hosts</span><br><span class="hljs-string">        git config --global user.name &quot;$GIT_NAME&quot;</span><br><span class="hljs-string">        git config --global user.email &quot;$GIT_EMAIL&quot;</span><br><span class="hljs-string">        yarn run hexo deploy</span><br></code></pre></td></tr></table></figure><p>只要配置了 hexo deploy 的都可以通过上面这种方式部署，注意如果是在其他 Pages 部署（比如Coding Pages 或者 码云 Pages），<code>ssh-keyscan</code> 需要进行增改：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># github、gitee 和 coding 三种 Pages 的示例，根据需求替换上例中语句，需要注意的是 coding 是使用二级域名。</span><br><span class="hljs-string">ssh-keyscan</span> <span class="hljs-string">github.com</span> <span class="hljs-string">&gt;&gt;</span> <span class="hljs-string">~/.ssh/known_hosts</span><br><span class="hljs-string">ssh-keyscan</span> <span class="hljs-string">gitee.com</span> <span class="hljs-string">&gt;&gt;</span> <span class="hljs-string">~/.ssh/known_hosts</span><br><span class="hljs-string">ssh-keyscan</span> <span class="hljs-string">e.coding.net</span> <span class="hljs-string">&gt;&gt;</span> <span class="hljs-string">~/.ssh/known_hosts</span><br></code></pre></td></tr></table></figure><p>然后 <code>${{ secrets.SSH_PRIVATE }}</code> 这种调用方式，需要提前在下图中设置常量：</p><p><img src="https://rmt.dogedoge.com/fetch/fluid/storage/actions-deploy/1.png?w=1280&amp;fmt=webp" alt=""></p><p>这样做可以避免敏感数据放在 yml 文件中被泄漏，即使你是私有仓库也建议这样做，因为设置的常量是无法被二次查看的，就算你账号被盗也不用担心。</p><h2 id="常用步骤配置"><a href="#常用步骤配置" class="headerlink" title="常用步骤配置"></a>常用步骤配置</h2><p>以上是以部署 hexo deploy 为例，下面再提供几种其他常见的部署配置，注意修改你自己的变量参数。</p><h3 id="阿里云-OSS"><a href="#阿里云-OSS" class="headerlink" title="阿里云 OSS"></a>阿里云 OSS</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">OSS</span><br>  <span class="hljs-attr">env:</span><br>    <span class="hljs-attr">OSS_AccessKeyID:</span> <span class="hljs-string">$&#123;&#123;</span> <span class="hljs-string">secrets.ACCESS_KEY_ID</span> <span class="hljs-string">&#125;&#125;</span><br>    <span class="hljs-attr">OSS_AccessKeySecret:</span> <span class="hljs-string">$&#123;&#123;</span> <span class="hljs-string">secrets.ACCESS_KEY_SECRET</span> <span class="hljs-string">&#125;&#125;</span><br>    <span class="hljs-attr">OSS_EndPoint:</span> <span class="hljs-string">oss-ap-southeast-1.aliyuncs.com</span><br>    <span class="hljs-attr">OSS_Bucket:</span> <span class="hljs-string">fluid-dev</span><br>  <span class="hljs-attr">run:</span> <span class="hljs-string">|</span><br><span class="hljs-string">    wget -q http://gosspublic.alicdn.com/ossutil/1.6.10/ossutil64</span><br><span class="hljs-string">    chmod +x ./ossutil64</span><br><span class="hljs-string">    ./ossutil64 config -e $OSS_EndPoint -i $OSS_AccessKeyID -k $OSS_AccessKeySecret -L CH</span><br><span class="hljs-string">    ./ossutil64 rm -r -f oss://$OSS_Bucket/</span><br><span class="hljs-string">    ./ossutil64 cp -r -f ./public oss://$OSS_Bucket/</span><br></code></pre></td></tr></table></figure><h3 id="腾讯云-COS"><a href="#腾讯云-COS" class="headerlink" title="腾讯云 COS"></a>腾讯云 COS</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">COS</span><br>  <span class="hljs-attr">uses:</span> <span class="hljs-string">zkqiang/tencent-cos-action@v0.1.0</span><br>  <span class="hljs-attr">with:</span><br>    <span class="hljs-attr">args:</span> <span class="hljs-string">delete</span> <span class="hljs-string">-r</span> <span class="hljs-string">-f</span> <span class="hljs-string">/</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">upload</span> <span class="hljs-string">-r</span> <span class="hljs-string">./public/</span> <span class="hljs-string">/</span><br>    <span class="hljs-attr">secret_id:</span> <span class="hljs-string">$&#123;&#123;</span> <span class="hljs-string">secrets.SECRET_ID</span> <span class="hljs-string">&#125;&#125;</span><br>    <span class="hljs-attr">secret_key:</span> <span class="hljs-string">$&#123;&#123;</span> <span class="hljs-string">secrets.SECRET_KEY</span> <span class="hljs-string">&#125;&#125;</span><br>    <span class="hljs-attr">bucket:</span> <span class="hljs-string">$&#123;&#123;</span> <span class="hljs-string">secrets.BUCKET</span> <span class="hljs-string">&#125;&#125;</span><br>    <span class="hljs-attr">region:</span> <span class="hljs-string">ap-shanghai</span><br></code></pre></td></tr></table></figure><h3 id="腾讯云开发"><a href="#腾讯云开发" class="headerlink" title="腾讯云开发"></a>腾讯云开发</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">Tencent</span> <span class="hljs-string">CloudBase</span><br>  <span class="hljs-attr">uses:</span> <span class="hljs-string">TencentCloudBase/cloudbase-action@v1.1.1</span><br>  <span class="hljs-attr">with:</span><br>    <span class="hljs-attr">secretId:</span> <span class="hljs-string">$&#123;&#123;</span> <span class="hljs-string">secrets.SECRET_ID</span> <span class="hljs-string">&#125;&#125;</span><br>    <span class="hljs-attr">secretKey:</span> <span class="hljs-string">$&#123;&#123;</span> <span class="hljs-string">secrets.SECRET_KEY</span> <span class="hljs-string">&#125;&#125;</span><br>    <span class="hljs-attr">envId:</span> <span class="hljs-string">$&#123;&#123;</span> <span class="hljs-string">secrets.ENV_ID</span> <span class="hljs-string">&#125;&#125;</span><br>    <span class="hljs-attr">staticSrcPath:</span> <span class="hljs-string">./public</span><br></code></pre></td></tr></table></figure><h3 id="服务器"><a href="#服务器" class="headerlink" title="服务器"></a>服务器</h3><p>如果是直接部署在服务器上，需要通过 FTP/SFTP 协议来完成上传操作，因此确保你的服务器开启了 FTP 服务。</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">Server</span><br>  <span class="hljs-attr">uses:</span> <span class="hljs-string">SamKirkland/FTP-Deploy-Action@3.1.1</span><br>  <span class="hljs-attr">with:</span><br>    <span class="hljs-attr">ftp-server:</span> <span class="hljs-string">$&#123;&#123;</span> <span class="hljs-string">secrets.FTP_SERVER</span> <span class="hljs-string">&#125;&#125;</span>      <span class="hljs-comment"># eg: ftp://ftp.xxx.com:22/mypath</span><br>    <span class="hljs-attr">ftp-username:</span> <span class="hljs-string">$&#123;&#123;</span> <span class="hljs-string">secrets.FTP_USERNAME</span> <span class="hljs-string">&#125;&#125;</span><br>    <span class="hljs-attr">ftp-password:</span> <span class="hljs-string">$&#123;&#123;</span> <span class="hljs-string">secrets.FTP_PASSWORD</span> <span class="hljs-string">&#125;&#125;</span><br>    <span class="hljs-attr">local-dir:</span> <span class="hljs-string">./public</span><br></code></pre></td></tr></table></figure>]]></content>
    
    
    <categories>
      
      <category>实用技巧</category>
      
    </categories>
    
    
    <tags>
      
      <tag>部署</tag>
      
      <tag>示例</tag>
      
      <tag>Hexo</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>mapboxgl导入模型时遇到的问题总结</title>
    <link href="/blog/posts/cb3db6e0.html"/>
    <url>/blog/posts/cb3db6e0.html</url>
    
    <content type="html"><![CDATA[<blockquote><p>前言：使用mapboxgl导入模型时，经常导不进去场景里面 会出很多差错，这里给大家分享一些心得。此文来源我同事小阮！</p></blockquote><h2 id="模型整合"><a href="#模型整合" class="headerlink" title="模型整合"></a>模型整合</h2><p>我们知道一个稍微一点复杂的模型，都是由很多很多的几何体组成的，不过你不把所有模型整合到一起可能导致在mapbox中无法加载的哟！<br>具体做法，如图一：<img src="/blog/posts/cb3db6e0/1.png" class="" title="图一"></p><span id="more"></span><p>随便选则一个小小的建筑合集（或者任何一个小的几何体）右键该模型将他转化为 可编辑多边形（或者可编辑网格）<br>选中该可编辑多边形 找到右边的属性栏 找到附加 功能，如图二:</p><p>点击附加，附加页面里面按 ctrl+a 全选，如图三：</p><p>这样他就变成了一个整体模型哟，如图四：</p><p>ps：如果下载的文件已经一个整体就不用这个操作了！！！</p><h2 id="材质整合"><a href="#材质整合" class="headerlink" title="材质整合"></a>材质整合</h2><p>不只是模型要整合哦，材质也要整合哦，不过这个过程实在是太复杂。总而言之就是 要把所有材质集合成一个多维子材质！具体教程可以百度经验里面有<a href="https://jingyan.baidu.com/article/afd8f4dedbd51234e286e9aa.html">https://jingyan.baidu.com/arti….cle/afd8f4dedbd51234</a><br>如果你的材质不是多维子材质 可能会导致 无法导致场景里面哟<br>ps：如果下载模型的材质已经是多维材质的话也不用这个操作啦！！</p><h2 id="模型导出"><a href="#模型导出" class="headerlink" title="模型导出"></a>模型导出</h2><p>般的话导出 obj 和 mtl 文件都会有一个选项建议参数这样选择哟，如图五。</p><p>导出完了之后 会有一个map文件夹，里面有你有的一些材质，这里注意一下所有的材质文件的名称必须要用中文！！！！！但是有的时候3dsmax会给你自己自动取名为中文，当你把中文去掉之后 你还要打开mtl文件（用vscode打开就可以）修改一下mtl里面的材质路径。说到这里 ，通过查看mtl文件还可以查看你的材质放在哪个目录这个也很关键哦！通常网上的一些作者材质都是乱放的，你可以通过修改mtl文件里面的路径 来找到你的材质文件。</p><h2 id="坐标系偏移问题"><a href="#坐标系偏移问题" class="headerlink" title="坐标系偏移问题"></a>坐标系偏移问题</h2><p>这个问题是今天碰到一个新问题，就是博阳在mapbox里面根本找不到文件在哪里，在cesium加载的模型实际坐标相差十万八千里。如图七：</p><p>当时我没有反应过来，知道博阳提到了我自己模型的坐标的问题，我才想起来，这个问题处理起来很简单很简单，只要几秒钟。选中模型按下W键 让模型变成可移动状态，然后按f11 把绝对：世界 下面的 xyz轴的参数全部调成0，这样模型就乖乖回到原点啦。<br>除此之外我想提到一个轴的概念，任何一个模型/物体 都有自己的轴，我们进行平移，旋转，缩放功能都是基于这个轴来进行的，默认情况下 这个轴是在 物体的中心的 但是今天下载的这个模型的轴呢偏离的特别夸张，如果你之后还想对这个模型进行缩放，旋转，平移操作的话建议把这个轴移到这个物体的中心，具体做法也非常简单百度一下就造了<br>ps 怎么判断这个物体是有偏移呢?不可能每一次都先导出了 然技术端试了偏了十万八千里再来改把，这样会给技术研发同事带来很多困扰<br>就像今天一样搞了一个小时才找到问题<br>这里有一个很简单的办法，就是每次我们打开3dsmax 主界面都会一个网格线 ，如图八：</p><p><img src="C:/Users/HelloGIS/Desktop/%E5%9B%BE%E5%85%AB.png" alt="图八"></p><p>如果你的模型在这个网格线上的话 就是标准的没有偏移很多哟</p><h2 id="渲染器问题"><a href="#渲染器问题" class="headerlink" title="渲染器问题"></a>渲染器问题</h2><p>当你确认了mtl文件没有问题，有模型框架缺没有材质，这个时候可能是的渲染器没有选对哦！现在市面上百分之八十3dsmax作者使用的都是vray渲染器，但是我们的mapbox场景不支持vray渲染器提供的材质和渲染效果，如果你购买的max模型使是用vray渲染器的话你要按f10调处渲染器窗口，将vray渲染器改成扫描线渲染器（默认渲染器），之后的材质颜色还是要靠你自己调整哟</p><p>备注：还有一个很简单的办法 可以看这个场景是否可以导入到mapbox场景里面哟！ 就是用3d查看器 直接打开glb/gltf格式的文件，如果材质和形状没有问题，说明 材质 和模型都可以正常显示（但是不能确定他的坐标问题！！今天我就是这样以为材质啥的没问题，结果偏到西班牙去了）<br>以后可能多多少少还有很多问题，希望大家可以互相学习，一起努力，总结经验 少走弯路！！</p>]]></content>
    
    
    
    <tags>
      
      <tag>Mapboxgl</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>搭配 Fluid 如何优雅的写一篇文章</title>
    <link href="/blog/posts/9604e2c7.html"/>
    <url>/blog/posts/9604e2c7.html</url>
    
    <content type="html"><![CDATA[<div class="note note-success">            <p>本文由 Fluid 用户授权转载，版权归原作者所有。</p> <p>本文作者：Vince<br>原文地址：<a href="https://i.vince.pub/posts/14677127/">https://i.vince.pub/posts/14677127/</a></p>           </div><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>Fluid 是一款很十分优雅的主题，那么写一篇优雅的文章搭配它呢？以下会从几个方面来简述，主要还是做几个推荐。</p><h2 id="文章内容"><a href="#文章内容" class="headerlink" title="文章内容"></a>文章内容</h2><h3 id="熟悉-Markdown-语法"><a href="#熟悉-Markdown-语法" class="headerlink" title="熟悉 Markdown 语法"></a>熟悉 Markdown 语法</h3><p>对于使用 Hexo 的大多数人来说，相信对 Markdown 的语法不会陌生。熟练掌握 Markdown 语法对我们的写作拥有极大的帮助，这里用少用的表格和脚注来举个例子。至于为什么有些公式、流程图无法渲染，是因为 <strong>Markdown 追求简洁式写作，默认渲染器不支持复杂渲染。</strong></p><h4 id="表格"><a href="#表格" class="headerlink" title="表格"></a>表格</h4><div class="table-container"><table><thead><tr><th>站点</th><th style="text-align:center">地址</th><th style="text-align:right">介绍</th></tr></thead><tbody><tr><td>Fluid Docs</td><td style="text-align:center"><a href="https://hexo.fluid-dev.com/docs/">https://hexo.fluid-dev.com/docs/</a></td><td style="text-align:right">Fluid 官方文档</td></tr><tr><td>Hexo-theme-fluid</td><td style="text-align:center"><a href="https://github.com/fluid-dev/hexo-theme-fluid">https://github.com/fluid-dev/hexo-theme-fluid</a></td><td style="text-align:right">Fluid Github Repo</td></tr><tr><td>Fluid Blog</td><td style="text-align:center"><a href="https://hexo.fluid-dev.com/">https://hexo.fluid-dev.com/</a></td><td style="text-align:right">Fluid 官方博客</td></tr></tbody></table></div><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs awk">站点|地址|介绍<br>--|:--:|--:<br>Fluid Docs|https:<span class="hljs-regexp">//</span>hexo.fluid-dev.com<span class="hljs-regexp">/docs/</span>|Fluid 官方文档<br>Hexo-theme-fluid|https:<span class="hljs-regexp">//gi</span>thub.com<span class="hljs-regexp">/fluid-dev/</span>hexo-theme-fluid|Fluid Github Repo<br>Fluid Blog|https:<span class="hljs-regexp">//</span>hexo.fluid-dev.com/|Fluid 官方博客<br></code></pre></td></tr></table></figure><h4 id="脚注"><a href="#脚注" class="headerlink" title="脚注"></a>脚注</h4><p>默认渲染器下正常显示，不同渲染器显示效果不同，写法如下：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs markdown">脚注演示[^1]<br>[<span class="hljs-symbol">^1</span>]: <span class="hljs-link">脚注内容演示</span><br></code></pre></td></tr></table></figure><h3 id="善用-HTML"><a href="#善用-HTML" class="headerlink" title="善用 HTML"></a>善用 HTML</h3><p>我们可以在 Markdown 中插入一些简单的 HTML 代码或 CSS 片段来获得更多扩展，使得文章内容更具有多样性。以下演示几个简单功能。</p><p><a id="demo">跳转位置演示（跳转位置设置点）</a></p><h4 id="文字颜色"><a href="#文字颜色" class="headerlink" title="文字颜色"></a>文字颜色</h4><p><span  style="color: #519D9E; ">#519D9E颜色演示</span></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span>  <span class="hljs-attr">style</span>=<span class="hljs-string">&quot;color: #519D9E; &quot;</span>&gt;</span>#519D9E颜色演示<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><br></code></pre></td></tr></table></figure><hr><h4 id="文字大小"><a href="#文字大小" class="headerlink" title="文字大小"></a>文字大小</h4><p><span  style="font-size:0.7em;">0.7em 文字大小演示</span></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span>  <span class="hljs-attr">style</span>=<span class="hljs-string">&quot;font-size:0.7em;&quot;</span>&gt;</span>0.7em 文字大小演示<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><br></code></pre></td></tr></table></figure><hr><h4 id="文字位置"><a href="#文字位置" class="headerlink" title="文字位置"></a>文字位置</h4><p style="text-align:center">内容居中演示</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">style</span>=<span class="hljs-string">&quot;text-align:center&quot;</span>&gt;</span>内容居中演示<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span> # 可以修改 text-align 参数来设置文字位置。<br></code></pre></td></tr></table></figure><hr><h4 id="页内跳转"><a href="#页内跳转" class="headerlink" title="页内跳转"></a>页内跳转</h4><p><a href="#demo">点击到达跳转位置演示</a></p><figure class="highlight livecodeserver"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs livecodeserver">&lt;<span class="hljs-keyword">a</span> href=<span class="hljs-string">&quot;#demo&quot;</span>&gt;点击到达跳转位置演示&lt;/<span class="hljs-keyword">a</span>&gt; <span class="hljs-comment"># 在需要跳转的地方添加此代码。</span><br>&lt;<span class="hljs-keyword">a</span> id=<span class="hljs-string">&quot;demo&quot;</span>&gt;跳转位置演示（跳转位置设置点）&lt;/<span class="hljs-keyword">a</span>&gt; <span class="hljs-comment"># 在跳转位置添加次代码。</span><br></code></pre></td></tr></table></figure><hr><h4 id="综合演示"><a href="#综合演示" class="headerlink" title="综合演示"></a>综合演示</h4><p style="text-align:center;color:#8EC0E4;font-size:1.5em;font-weight: bold;">综合演示<br>优雅使用 Fluid 写文章</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">style</span>=<span class="hljs-string">&quot;text-align:center;color:#8EC0E4;font-size:1.5em;font-weight: bold;&quot;</span>&gt;</span><br>综合演示<br><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span><br>优雅使用 Fluid 写文章<br><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span><br><br></code></pre></td></tr></table></figure><h4 id="iframe-页面镶套"><a href="#iframe-页面镶套" class="headerlink" title="iframe 页面镶套"></a>iframe 页面镶套</h4><p>iframe 页面镶套可以帮助我们更好的展示一个页面。比如以下演示页面。</p><iframe src="https://hexo.fluid-dev.com/" width="100%" height="650" name="topFrame" scrolling="yes"  noresize="noresize" frameborder="0" id="topFrame"></iframe><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs routeros">&lt;iframe <span class="hljs-attribute">src</span>=<span class="hljs-string">&quot;https://hexo.fluid-dev.com/&quot;</span> <span class="hljs-attribute">width</span>=<span class="hljs-string">&quot;100%&quot;</span> <span class="hljs-attribute">height</span>=<span class="hljs-string">&quot;500&quot;</span> <span class="hljs-attribute">name</span>=<span class="hljs-string">&quot;topFrame&quot;</span> <span class="hljs-attribute">scrolling</span>=<span class="hljs-string">&quot;yes&quot;</span>  <span class="hljs-attribute">noresize</span>=<span class="hljs-string">&quot;noresize&quot;</span> <span class="hljs-attribute">frameborder</span>=<span class="hljs-string">&quot;0&quot;</span> <span class="hljs-attribute">id</span>=<span class="hljs-string">&quot;topFrame&quot;</span>&gt;&lt;/iframe&gt;<br></code></pre></td></tr></table></figure><p>一些参数说明，<code>width=&quot;100%&quot;</code> 为宽度自适应，高度请根据实际需求跳转，<strong>注意移动端页面是否匹配。</strong> <code>scrolling</code> 为滚动条参数。<code>frameborder</code> 为边框参数。<code>noresize</code> 属性规定用户无法调整框架的大小。</p><h3 id="善用-Tag-组件"><a href="#善用-Tag-组件" class="headerlink" title="善用 Tag 组件"></a>善用 Tag 组件</h3><p>Fluid 内置了许多 Tag 组件，包含便签、行内标签、勾选框、按钮和组图，可以使用这些组件来丰富文章内容，具体点击查看官方文档查看，<strong><a href="https://hexo.fluid-dev.com/docs/guide/#tag-%E6%8F%92%E4%BB%B6">点击跳转到 Fluid Doc</a></strong>。</p><h2 id="配图"><a href="#配图" class="headerlink" title="配图"></a>配图</h2><p>众所周知，<strong>博客好不好看，配图占一半</strong>。这里给大家推荐几个我常用找配图的地方。<strong>另外，请遵循对于网站的版权协议。</strong></p><h3 id="Wallpaper-Hub"><a href="#Wallpaper-Hub" class="headerlink" title="Wallpaper Hub"></a>Wallpaper Hub</h3><p><img src="https://cdn.vince.pub/blog-file/photo/2020-04-17_175244.png" alt="Wallpaper Hub"></p><p><strong><a href="https://wallpaperhub.app/">点击跳转到 Wallpaper Hub</a></strong></p><h3 id="Wallhaven"><a href="#Wallhaven" class="headerlink" title="Wallhaven"></a>Wallhaven</h3><p><img src="https://cdn.vince.pub/blog-file/photo/2020-04-17_174841.png" alt="Wallhaven"></p><p><strong><a href="https://wallhaven.cc/">点击跳转到 Wallhaven</a></strong></p><h3 id="Unsplash"><a href="#Unsplash" class="headerlink" title="Unsplash"></a>Unsplash</h3><p><img src="https://cdn.vince.pub/blog-file/photo/2020-05-14_000557.png" alt="Unsplash"></p><p><strong><a href="https://unsplash.com/">点击跳转到 Wallhaven</a></strong></p>]]></content>
    
    
    <categories>
      
      <category>主题示例</category>
      
    </categories>
    
    
    <tags>
      
      <tag>示例</tag>
      
      <tag>用户经验</tag>
      
      <tag>Fluid</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>使用 Rainbow 展示随机的英语句子</title>
    <link href="/blog/posts/9604e2c3.html"/>
    <url>/blog/posts/9604e2c3.html</url>
    
    <content type="html"><![CDATA[<div class="note note-success">            <p>本文由 Fluid 用户授权转载，版权归原作者所有。</p> <p>本文作者：吃白饭<br>原文地址：<a href="https://eatrice.top/post/Rainbow/">https://eatrice.top/post/Rainbow/</a></p>           </div><p>访问 <a href="https://rainbow.eatrice.top/">Rainbow</a> 吧！</p><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>Rainbow - 一朵彩虹是 <code>EatRiceTeam</code>建立的一个旨在收集优美英语句子的一套网站。演示网站地址为：<a href="https://rainbow.eatrice.top/">https://rainbow.eatrice.top/</a></p><p>我们希望能够与大家分享我们在日常的学习生活中遇到的优美的英语句子，希望它能像彩虹一样，美丽天空，温暖人心。</p><p>其由<code>C#</code>开发，基于<code>ASP.NET Core 2.2</code>框架。包括<code>Web API</code>提供导出<code>JSON</code>的数据接口，和基于<code>MVC</code>的动态展示网站。</p><h2 id="关于Rainbow"><a href="#关于Rainbow" class="headerlink" title="关于Rainbow"></a>关于Rainbow</h2><p>Rainbow 收集的英语句子的要求为：</p><ol><li>读起来感觉很优美的文章句子段落或诗歌节选；</li><li>含义特别丰富且引人深思的鸡汤或哲学句子；</li><li>句子奇怪，但意义完整且显得很有个性的电影台词；</li><li>你特别喜欢，且引起你感情上共鸣的英语歌词。</li></ol><p>Rainbow 创建的初衷是替代我们的个人网站目前正在使用的 一言 ，我们希望自己能够自己定义一句话的意思和表现形式。目前句子库不是特别丰富，收集的资源有限，所以欢迎大家投稿，并发表自己的看法。</p><h2 id="给Rainbow投稿"><a href="#给Rainbow投稿" class="headerlink" title="给Rainbow投稿"></a>给Rainbow投稿</h2><p>我们希望找到小伙伴们和我们一起充实我们的句子库，希望大家能够将自己珍藏的句子分享给我们：</p><p><strong>投稿要求</strong>：</p><ol><li>提供完整的句子</li><li>提供句子的作者</li><li>提供句子的来源，如书名、文章名、电影名、歌曲名等。</li></ol><p>投稿方式：</p><ol><li><p><a href="mailto:qiqi@eatrice.top">EatRice的邮箱：qiqi@eatrice.top</a></p></li><li><p><a href="dipper.ruru@gmail.com">Courir的邮箱：dipper.ruru@gmail.com</a></p></li><li><p>在本页下方留言</p></li><li><p>好友QQ或微信直接发送</p></li></ol><h2 id="使用方法"><a href="#使用方法" class="headerlink" title="使用方法"></a>使用方法</h2><h3 id="数据接口"><a href="#数据接口" class="headerlink" title="数据接口"></a>数据接口</h3><p>目前语句库饱含了三种类型的语句：reading、movies、songs</p><p>需要从语句库中随机获得语句的<code>Json</code>格式的接口：<code>https://api.eatrice.top/</code></p><p>需要按照三个单独分类请求语句的接口：<br><code>https://api.eatrice.top/reading/</code><br><code>https://api.eatrice.top/movies/</code><br><code>https://api.eatrice.top/songs/</code></p><p>获取所有的句子接口：<code>https://api.eatrice.top/GetAll/</code></p><p>需要根据语句ID请求语句的接口：<code>https://api.eatrice.top/?ID=10001</code><br>ID编号从10001开始增加，若该ID不存在则随机返回语句，同<code>https://api.eatrice.top/</code></p><p>返回的数据格式如下：<br><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs json">&#123;<br>    <span class="hljs-attr">&quot;Content&quot;</span>: <span class="hljs-string">&quot;Because I am your mom,It counts the most because I know you the most.&quot;</span>,<br>    <span class="hljs-attr">&quot;Author&quot;</span>: <span class="hljs-string">&quot;Stephen Chbosky&quot;</span>,<br>    <span class="hljs-attr">&quot;Source&quot;</span>: <span class="hljs-string">&quot;Wonder&quot;</span>,<br>    <span class="hljs-attr">&quot;ID&quot;</span>: <span class="hljs-string">&quot;10009&quot;</span><br>&#125;<br></code></pre></td></tr></table></figure><br>其中，<code>Content</code>为句子内容<br><code>Author</code>为句子作者<br><code>Source</code>为句子来源<br><code>ID</code>为句子ID</p><h3 id="展示网站"><a href="#展示网站" class="headerlink" title="展示网站"></a>展示网站</h3><p>展示网站为 Rainbow 提供展示界面。和说明文档。</p><p>Rainbow的展示网站为：<a href="https://rainbow.eatrice.top/">https://rainbow.eatrice.top/</a><br>欢迎大家访问和提供意见😊😊😊。</p><p><img src="https://rmt.dogedoge.com/fetch/fluid/storage/hexo-rainbow/1.png?w=1280&amp;fmt=webp" alt="效果图"></p><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p>在需要添加 <code>rainbow</code> 的地方添加一个占位符:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;rainbow&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&#x27;&#x27;</span>&gt;</span>🌈 获取中...<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span><br></code></pre></td></tr></table></figure><p>在申明div之后，搭配数据请求脚本</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript"></span><br><span class="javascript">fetch(<span class="hljs-string">&#x27;https://api.eatrice.top&#x27;</span>)</span><br><span class="javascript">  .then(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> response.json())</span><br><span class="javascript">  .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> &#123;</span><br><span class="javascript">    <span class="hljs-keyword">var</span> rainbow = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">&#x27;rainbow&#x27;</span>);</span><br><span class="javascript">    rainbow.innerHTML = data.Content;</span><br><span class="javascript">    rainbow.href = <span class="hljs-string">&quot;https://rainbow.eatrice.top/?ID=&quot;</span> + data.ID;</span><br><span class="javascript">  &#125;)</span><br><span class="javascript">  .catch(<span class="hljs-built_in">console</span>.error)</span><br><span class="javascript"></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br></code></pre></td></tr></table></figure><p>就能在网站上看到你的 rainbow 啦！</p><h2 id="开源"><a href="#开源" class="headerlink" title="开源"></a>开源</h2><p>项目已在<code>github</code>上开源</p><ul><li>项目地址：<a href="https://github.com/QiQiWan/rainbow/">https://github.com/QiQiWan/rainbow/</a></li><li>项目仓库：<a href="https://github.com/QiQiWan/rainbow/">git@github.com:QiQiWan/rainbow.git</a></li></ul><h2 id="贡献者"><a href="#贡献者" class="headerlink" title="贡献者"></a>贡献者</h2><p><a href="https://eatrice.top">EatRice-https://eatrice.top</a><br><a href="https://ruru.eatrice.top">上屋顶看北斗七星-https://ruru.eatrice.top</a></p>]]></content>
    
    
    <categories>
      
      <category>功能增强</category>
      
    </categories>
    
    
    <tags>
      
      <tag>Hexo</tag>
      
      <tag>用户经验</tag>
      
      <tag>花里胡哨</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>Hexo 暗黑模式</title>
    <link href="/blog/posts/9604e2c5.html"/>
    <url>/blog/posts/9604e2c5.html</url>
    
    <content type="html"><![CDATA[<div class="note note-success">            <p>本文由 Fluid 用户授权转载，版权归原作者所有。</p> <p>本文作者：Royce<br>原文地址：<a href="https://royce2003.top/posts/41212.html">https://royce2003.top/posts/41212.html</a></p>           </div><p>大概花了一个晚上搞暗黑模式，之后陆续优化了下<br>目前博客已经基本上适配完成了<br>目前是三种方案（优先级递减）</p><ol><li>媒体查询</li><li>定时开启</li><li>localStorage/sessionStorage 查询</li></ol><p><code>媒体查询</code>，判断系统是否处于暗黑模式，支持大部分系统<br>Win10 需要浏览器开启软件深色模式<br>Android 同理，需要浏览器支持手机开启夜间模式的时候将自身切换到神色模式，目前 Chrome 支持，Edge 不支持，其他没测<br>iOS、MacOS 上的 Safari 也支持</p><p><code>定时开启</code>，在规定时间自动开启，如果在该时间段内取消了暗黑模式，能一直保持</p><p><code>localStorage/sessionStorage 查询</code>，能一直保持某一个模式的依赖</p><h3 id="HTML"><a href="#HTML" class="headerlink" title="HTML"></a>HTML</h3><p>在 <code>\themes\fluid\layout\layout.ejs</code> 中找到 <code>&lt;body&gt;</code>，在其之后加入如下代码</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;dark&quot;</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">&quot;switchDarkMode()&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript"></span><br><span class="javascript">  <span class="hljs-keyword">var</span> isNight = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getHours() &gt;= <span class="hljs-number">22</span> || <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getHours() &lt; <span class="hljs-number">7</span>; <span class="hljs-comment">// 指定时间</span></span><br><span class="javascript">  <span class="hljs-comment">// 依次判断 系统暗黑模式 指定时间 缓存 dark</span></span><br><span class="javascript">  <span class="hljs-keyword">if</span>( matchMedia(<span class="hljs-string">&#x27;(prefers-color-scheme: dark)&#x27;</span>).matches || isNight || <span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">&#x27;dark&#x27;</span>) === <span class="hljs-string">&#x27;1&#x27;</span>) &#123;</span><br><span class="javascript">    <span class="hljs-keyword">if</span>(!(isNight&amp;&amp;<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">&#x27;noDark&#x27;</span>) === <span class="hljs-string">&#x27;1&#x27;</span>)) &#123;</span><br><span class="javascript">      <span class="hljs-built_in">document</span>.body.classList.add(<span class="hljs-string">&#x27;dark&#x27;</span>);</span><br><span class="javascript">    &#125;</span><br><span class="javascript">  &#125;</span><br><span class="javascript">  <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">&#x27;dark&#x27;</span>).innerHTML = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">&quot;body&quot;</span>).classList.contains(<span class="hljs-string">&quot;dark&quot;</span>)?<span class="hljs-string">&quot;🌙&quot;</span>:<span class="hljs-string">&quot;🌞&quot;</span>;</span><br><span class="javascript"></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br></code></pre></td></tr></table></figure><div class="note note-danger">            <p>注意！一定紧跟在 <code>body</code> 标签之后，否则会出现闪烁</p>           </div><h3 id="JS"><a href="#JS" class="headerlink" title="JS"></a>JS</h3><p>在自定义 JS 中把下面代码加进去，直接加到 <code>&lt;/body&gt;</code> 之前也行</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">//点击事件</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">switchDarkMode</span>(<span class="hljs-params"></span>) </span>&#123;<br><span class="hljs-keyword">if</span> ($(<span class="hljs-string">&#x27;body&#x27;</span>).hasClass(<span class="hljs-string">&#x27;dark&#x27;</span>)) &#123;<br>$(<span class="hljs-string">&quot;#dark&quot;</span>).html(<span class="hljs-string">&quot;🌞&quot;</span>);<br><span class="hljs-built_in">document</span>.body.classList.remove(<span class="hljs-string">&#x27;dark&#x27;</span>);<br><span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">&#x27;noDark&#x27;</span>, <span class="hljs-string">&#x27;1&#x27;</span>);<br><span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">&#x27;dark&#x27;</span>, <span class="hljs-string">&#x27;0&#x27;</span>);<br>&#125; <span class="hljs-keyword">else</span> &#123;<br>$(<span class="hljs-string">&quot;#dark&quot;</span>).html(<span class="hljs-string">&quot;🌙&quot;</span>); <br><span class="hljs-built_in">document</span>.body.classList.add(<span class="hljs-string">&#x27;dark&#x27;</span>);<br><span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">&#x27;dark&#x27;</span>, <span class="hljs-string">&#x27;1&#x27;</span>);<br><span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">&#x27;noDark&#x27;</span>, <span class="hljs-string">&#x27;0&#x27;</span>);<br>&#125;<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="CSS"><a href="#CSS" class="headerlink" title="CSS"></a>CSS</h3><p>在自定义 CSS 中加入代码</p><div class="note note-primary">            <p>可以用 <code>stylus</code>，能少些写<br>但是引入时记得后缀还是 <code>.css</code> 不要变</p>           </div><p>下面是我的样式代码，基本覆盖所有内容<br>有配上些注释，根据自身情况修改，注意缩进</p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br></pre></td><td class="code"><pre><code class="hljs stylus"><span class="hljs-comment">/* 切换按钮 */</span><br><span class="hljs-selector-id">#dark</span><br>  <span class="hljs-attribute">cursor</span> pointer<br>  <span class="hljs-attribute">position</span> fixed<br>  <span class="hljs-attribute">right</span> <span class="hljs-number">40px</span><br>  <span class="hljs-attribute">bottom</span> <span class="hljs-number">98px</span><br>  <span class="hljs-attribute">width</span> <span class="hljs-number">16px</span><br>  <span class="hljs-attribute">height</span> <span class="hljs-number">14px</span><br>  <span class="hljs-attribute">z-index</span> <span class="hljs-number">100</span><br>  <span class="hljs-attribute">font-size</span> <span class="hljs-number">20px</span><br><br><span class="hljs-comment">/*暗黑模式*/</span><br><span class="hljs-selector-class">.dark</span><br>  <span class="hljs-attribute">background-color</span> <span class="hljs-number">#282c34</span><br><br>  <span class="hljs-comment">/* 背景遮罩 */</span><br>  <span class="hljs-selector-class">.mask</span><br>    <span class="hljs-attribute">background-color</span> rgba(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,.<span class="hljs-number">7</span>) <span class="hljs-meta">!important</span><br><br>  <span class="hljs-comment">/* 主体 */</span><br>  <span class="hljs-selector-id">#board</span> <br>    <span class="hljs-attribute">background-color</span> <span class="hljs-number">#282c34</span><br>    <span class="hljs-attribute">color</span> <span class="hljs-number">#a09c9c</span><br>  <br>  <span class="hljs-selector-tag">img</span>  <br>    <span class="hljs-attribute">filter</span> brightness(<span class="hljs-number">50%</span>) // 图片亮度<br><br>  <span class="hljs-selector-tag">p</span><br>  <span class="hljs-selector-class">.index-info</span> <span class="hljs-selector-tag">a</span>  <br>    <span class="hljs-attribute">color</span> <span class="hljs-number">#a09c9c</span> <span class="hljs-meta">!important</span><br><br>  <span class="hljs-selector-class">.markdown-body</span><br>    <span class="hljs-selector-tag">h1</span>,<span class="hljs-selector-tag">h2</span>,<span class="hljs-selector-tag">h3</span>,<span class="hljs-selector-tag">h4</span>,<span class="hljs-selector-tag">h5</span>,<span class="hljs-selector-tag">h6</span>,s,<span class="hljs-selector-tag">li</span>  <br>      <span class="hljs-attribute">color</span>:<span class="hljs-number">#a09c9c</span> <span class="hljs-meta">!important</span><br>    <br><br>  <span class="hljs-comment">/* 顶栏 */</span><br>  <span class="hljs-selector-class">.navbar-col-show</span><br>  <span class="hljs-selector-class">.top-nav-collapse</span>  <br>    <span class="hljs-attribute">background-color</span> <span class="hljs-number">#282c34</span><br>    <br>  <span class="hljs-selector-class">.navbar</span> <span class="hljs-selector-tag">a</span>  <br>    <span class="hljs-attribute">color</span> <span class="hljs-number">#a09c9c</span> <span class="hljs-meta">!important</span><br>    <br>  <span class="hljs-selector-class">.animated-icon</span> <span class="hljs-selector-tag">span</span>   <span class="hljs-comment">/* 手机端 */</span><br>    <span class="hljs-attribute">background-color</span> <span class="hljs-number">#a09c9c</span><br><br><br>  <span class="hljs-comment">/* page-number */</span><br>  <span class="hljs-selector-class">.pagination</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span><br>  <span class="hljs-selector-class">.pagination</span> <span class="hljs-selector-class">.current</span>  <br>    <span class="hljs-attribute">background-color</span> <span class="hljs-number">#6b6b6b</span>73;<br><br><br>  <span class="hljs-comment">/* 打字机 */</span><br>  <span class="hljs-selector-id">#subtitle</span><br>  <span class="hljs-selector-class">.dark</span><span class="hljs-selector-class">.typed-cursor--blink</span><br>  <span class="hljs-selector-class">.scroll-down-arrow</span><br>    <span class="hljs-attribute">color</span> <span class="hljs-number">#dfdfdf</span><br><br><br>  <span class="hljs-comment">/* back to top */</span><br>  <span class="hljs-selector-id">#scroll-top-button</span><br>    <span class="hljs-attribute">background-color</span> <span class="hljs-number">#282c34</span><br><br>    <span class="hljs-selector-tag">i</span><br>      <span class="hljs-attribute">color</span> <span class="hljs-number">#a09c9c</span><br>    <br><br>  <span class="hljs-comment">/* Toc */</span><br>  <span class="hljs-selector-class">.tocbot-list</span> <span class="hljs-selector-tag">a</span><br>    <span class="hljs-attribute">color</span> <span class="hljs-number">#a09c9c</span><br><br>  <span class="hljs-selector-class">.tocbot-active-link</span><br>  <span class="hljs-selector-tag">footer</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span><br>    <span class="hljs-attribute">color</span> <span class="hljs-number">#1abc9c</span> <span class="hljs-meta">!important</span><br><br><br>  <span class="hljs-comment">/* footer */</span><br>  <span class="hljs-selector-tag">footer</span><br>  <span class="hljs-selector-tag">footer</span> <span class="hljs-selector-tag">a</span><br>    <span class="hljs-attribute">color</span> <span class="hljs-number">#a09c9c</span><br>    <br><br>  <span class="hljs-comment">/* 归档页 */</span><br>  <span class="hljs-selector-class">.list-group-item</span><br>    <span class="hljs-attribute">color</span> <span class="hljs-number">#a09c9c</span><br>    <span class="hljs-attribute">background-color</span> <span class="hljs-number">#282c34</span><br>    <br>  <span class="hljs-selector-class">.list-group-item</span><span class="hljs-selector-pseudo">:hover</span><br>  <span class="hljs-selector-class">.tagcloud</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span><br>    <span class="hljs-attribute">background-color</span> <span class="hljs-number">#46484d</span><br><br><br>  <span class="hljs-comment">/* 友链页 */</span><br>  <span class="hljs-selector-class">.links</span><br>    <span class="hljs-selector-class">.card</span>  <br>      <span class="hljs-attribute">background-color</span> <span class="hljs-number">#282c34</span><br>        <br>    <span class="hljs-selector-class">.card-body</span><span class="hljs-selector-pseudo">:hover</span>  <br>      <span class="hljs-attribute">background-color</span> <span class="hljs-number">#46484d</span><br>        <br>    <span class="hljs-selector-class">.link-title</span><br>    <span class="hljs-selector-class">.link-intro</span>  <br>      <span class="hljs-attribute">color</span> <span class="hljs-number">#a09c9c</span><br>    <br><br>  <span class="hljs-comment">/* note标签 配色有点丑 */</span><br>  <span class="hljs-selector-class">.note-info</span><br>    <span class="hljs-attribute">background-color</span> <span class="hljs-number">#3b5359</span><br>    <span class="hljs-attribute">border-color</span> <span class="hljs-number">#006d80</span><br><br>  <span class="hljs-selector-class">.note-danger</span><br>    <span class="hljs-attribute">background-color</span> <span class="hljs-number">#783f42</span><br>    <span class="hljs-attribute">border-color</span> <span class="hljs-number">#670009</span><br><br>  <span class="hljs-selector-class">.note-success</span><br>    <span class="hljs-attribute">background-color</span> <span class="hljs-number">#2a3e2e</span><br>    <span class="hljs-attribute">border-color</span> <span class="hljs-number">#005915</span><br><br>  <span class="hljs-selector-class">.note-warning</span><br>    <span class="hljs-attribute">background-color</span> <span class="hljs-number">#5b543e</span><br>    <span class="hljs-attribute">border-color</span> <span class="hljs-number">#846500</span><br><br>  <span class="hljs-selector-class">.note-primary</span><br>    <span class="hljs-attribute">background-color</span> <span class="hljs-number">#455a6f</span><br>    <span class="hljs-attribute">border-color</span> <span class="hljs-number">#004188</span><br></code></pre></td></tr></table></figure><h3 id="localStorage"><a href="#localStorage" class="headerlink" title="localStorage"></a>localStorage</h3><p>仔细观察刚刚的 js 代码，在其中用到了 localStorage<br>相当于一个标记，除非被手动清除，否则将会永久保存。</p><p>下面是支持该特性的最低版本</p><p><img src="https://rmt.dogedoge.com/fetch/royce/storage/darkmode/01.png?w=1280&amp;fmt=webp" alt=""></p><p>可以在浏览器控制台中查看他们的值</p><p><img src="https://rmt.dogedoge.com/fetch/royce/storage/darkmode/02.png?w=1280&amp;fmt=webp" alt=""></p><hr><p>参考 <a href="https://crosschannel.cc/daily/hexo%E6%B7%BB%E5%8A%A0%E6%9A%97%E8%89%B2%E6%A8%A1%E5%BC%8F.html">https://crosschannel.cc/daily/hexo添加暗色模式.html</a></p>]]></content>
    
    
    <categories>
      
      <category>功能增强</category>
      
    </categories>
    
    
    <tags>
      
      <tag>Hexo</tag>
      
      <tag>用户经验</tag>
      
      <tag>花里胡哨</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>给 Hexo 博客添加 PWA 支持</title>
    <link href="/blog/posts/9604e2a5.html"/>
    <url>/blog/posts/9604e2a5.html</url>
    
    <content type="html"><![CDATA[<div class="note note-success">            <p>本文由 Fluid 用户授权转载，版权归原作者所有。</p> <p>本文作者：吃白饭<br>原文地址：<a href="https://eatrice.top/post/给hexo博客添加PWA支持/">https://eatrice.top/post/给hexo博客添加PWA支持/</a></p>           </div><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>PWA（Progressive Web App）的中文名叫做「渐进式网页应用」，早在2014年， W3C 公布过 Service Worker 的相关草案，但是其在生产环境被 Chrome 支持是在 2015 年。因此，如果我们把 PWA 的关键技术之一 Service Worker 的出现作为 PWA 的诞生时间，那就应该是 2015 年。</p><p>自 2015 年以来，PWA 相关的技术不断升级优化，在用户体验和用户留存两方面都提供了非常好的解决方案。PWA 可以将 Web 和 App 各自的优势融合在一起：渐进式、可响应、可离线、实现类似 App 的交互、即时更新、安全、可以被搜索引擎检索、可推送、可安装、可链接。[1]</p><p>由于 Hexo 为静态博客，因此不需要具备推送功能（其实是我没搞懂）。因此PWA的特性包括其渐进式、可离线，可以作为提高网站体验和提高网站家在速度的一个方法。因此下面将从其主要内容和hexo如何安装两个方面以“吃白饭的休伯利安号”为例来简单演示一遍安装过程。</p><h2 id="内容"><a href="#内容" class="headerlink" title="内容"></a>内容</h2><h3 id="渐进式"><a href="#渐进式" class="headerlink" title="渐进式"></a>渐进式</h3><p>什么是渐进式，即将传统的web应用，应用现代的技术和方法使之在能够有桌面应用一般的体验，即为渐进式web应用。渐进式web应用可以同时运行在传统的浏览器上，像普通的网站一样进行浏览和操作；其同时也可以运行在现代功能完善的浏览器中，可以使其具备更多的效果和功能。比较常见的有<strong>可安装</strong>，即在支持的浏览器和操作系统上可以生成访问图标，通过图标可以可桌面应用一样访问应用；<strong>消息推送</strong>，即访问应用时服务器端可以通过应用的后台进程主动向客户端推送消息，类似于桌面应用的消息队列。</p><h3 id="可离线"><a href="#可离线" class="headerlink" title="可离线"></a>可离线</h3><p>支持应用离线访问，即正常访问应用时，后台进程会自动缓存内容，下次访问时应用优先从缓存区读取数据，然后是进行web请求。因此可离线实质上充当了web代理服务器的职责，先是将正常请求代理到缓存区，再是将缓存区不足的文件进行正常的网络请求，通过此方法实现了离线的目标。根据可离线的规律，应用在一次访问缓存之后二次访问即可断网。</p><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><h3 id="Web-app-manifest"><a href="#Web-app-manifest" class="headerlink" title="Web app manifest"></a>Web app manifest</h3><p>首先要实现PWA的可安装性，需要有一个清单文件<code>manifest.json</code>。<code>manifest.json</code>是一个简单的<code>json</code>文件，它描述了我们的图标在主屏幕上如何显示，以及图标点击进去的启动页是什么，自动生成<code>manifest.json</code>的工具：<a href="https://app-manifest.firebaseapp.com/">manifest.json生成工具</a>（需要梯子），本站的JSON格式如下所示：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><code class="hljs json">&#123;<br>  <span class="hljs-attr">&quot;name&quot;</span>: <span class="hljs-string">&quot;吃白饭的休伯利安号&quot;</span>,<br>  <span class="hljs-attr">&quot;short_name&quot;</span>: <span class="hljs-string">&quot;吃白饭博客&quot;</span>,<br>  <span class="hljs-attr">&quot;theme_color&quot;</span>: <span class="hljs-string">&quot;#3a311c&quot;</span>,<br>  <span class="hljs-attr">&quot;background_color&quot;</span>: <span class="hljs-string">&quot;#3a311c&quot;</span>,<br>  <span class="hljs-attr">&quot;display&quot;</span>: <span class="hljs-string">&quot;standalone&quot;</span>,<br>  <span class="hljs-attr">&quot;Scope&quot;</span>: <span class="hljs-string">&quot;/&quot;</span>,<br>  <span class="hljs-attr">&quot;start_url&quot;</span>: <span class="hljs-string">&quot;/&quot;</span>,<br>  <span class="hljs-attr">&quot;icons&quot;</span>: [<br>    &#123;<br>      <span class="hljs-attr">&quot;src&quot;</span>: <span class="hljs-string">&quot;/images/icons/icon-72x72.png&quot;</span>,<br>      <span class="hljs-attr">&quot;sizes&quot;</span>: <span class="hljs-string">&quot;72x72&quot;</span>,<br>      <span class="hljs-attr">&quot;type&quot;</span>: <span class="hljs-string">&quot;image/png&quot;</span><br>    &#125;,<br>    &#123;<br>      <span class="hljs-attr">&quot;src&quot;</span>: <span class="hljs-string">&quot;/images/icons/icon-96x96.png&quot;</span>,<br>      <span class="hljs-attr">&quot;sizes&quot;</span>: <span class="hljs-string">&quot;96x96&quot;</span>,<br>      <span class="hljs-attr">&quot;type&quot;</span>: <span class="hljs-string">&quot;image/png&quot;</span><br>    &#125;,<br>    &#123;<br>      <span class="hljs-attr">&quot;src&quot;</span>: <span class="hljs-string">&quot;/images/icons/icon-128x128.png&quot;</span>,<br>      <span class="hljs-attr">&quot;sizes&quot;</span>: <span class="hljs-string">&quot;128x128&quot;</span>,<br>      <span class="hljs-attr">&quot;type&quot;</span>: <span class="hljs-string">&quot;image/png&quot;</span><br>    &#125;,<br>    &#123;<br>      <span class="hljs-attr">&quot;src&quot;</span>: <span class="hljs-string">&quot;/images/icons/icon-144x144.png&quot;</span>,<br>      <span class="hljs-attr">&quot;sizes&quot;</span>: <span class="hljs-string">&quot;144x144&quot;</span>,<br>      <span class="hljs-attr">&quot;type&quot;</span>: <span class="hljs-string">&quot;image/png&quot;</span><br>    &#125;,<br>    &#123;<br>      <span class="hljs-attr">&quot;src&quot;</span>: <span class="hljs-string">&quot;/images/icons/icon-152x152.png&quot;</span>,<br>      <span class="hljs-attr">&quot;sizes&quot;</span>: <span class="hljs-string">&quot;152x152&quot;</span>,<br>      <span class="hljs-attr">&quot;type&quot;</span>: <span class="hljs-string">&quot;image/png&quot;</span><br>    &#125;,<br>    &#123;<br>      <span class="hljs-attr">&quot;src&quot;</span>: <span class="hljs-string">&quot;/images/icons/icon-192x192.png&quot;</span>,<br>      <span class="hljs-attr">&quot;sizes&quot;</span>: <span class="hljs-string">&quot;192x192&quot;</span>,<br>      <span class="hljs-attr">&quot;type&quot;</span>: <span class="hljs-string">&quot;image/png&quot;</span><br>    &#125;,<br>    &#123;<br>      <span class="hljs-attr">&quot;src&quot;</span>: <span class="hljs-string">&quot;/images/icons/icon-384x384.png&quot;</span>,<br>      <span class="hljs-attr">&quot;sizes&quot;</span>: <span class="hljs-string">&quot;384x384&quot;</span>,<br>      <span class="hljs-attr">&quot;type&quot;</span>: <span class="hljs-string">&quot;image/png&quot;</span><br>    &#125;,<br>    &#123;<br>      <span class="hljs-attr">&quot;src&quot;</span>: <span class="hljs-string">&quot;/images/icons/icon-512x512.png&quot;</span>,<br>      <span class="hljs-attr">&quot;sizes&quot;</span>: <span class="hljs-string">&quot;512x512&quot;</span>,<br>      <span class="hljs-attr">&quot;type&quot;</span>: <span class="hljs-string">&quot;image/png&quot;</span><br>    &#125;<br>  ],<br>  <span class="hljs-attr">&quot;splash_pages&quot;</span>: <span class="hljs-literal">null</span><br>&#125;<br><br></code></pre></td></tr></table></figure><p>其中：</p><blockquote><ul><li>start_url 可以设置启动网址</li><li>icons 可以设置各个分辨率下页面的图标，适配不同的尺寸的路径</li><li>background_color 会设置背景颜色， Chrome 在网络应用启动后会立即使用此颜色，这一颜色将保留在屏幕上，直至网络应用首次呈现为止。</li><li>theme_color 会设置主题颜色</li><li>display 设置启动样式</li></ul></blockquote><p>配置好<code>manifest.json</code>后进行调试，打开浏览器的控制台如下图所示，即文件配置成功。</p><p><img src="https://rmt.dogedoge.com/fetch/fluid/storage/hexo-pwa/1.png?w=1280&amp;fmt=webp" alt=""></p><h3 id="离线使用"><a href="#离线使用" class="headerlink" title="离线使用"></a>离线使用</h3><p>离线使用依赖<code>Service Work</code>，其本质是一段运行在并行于主进程的后台进程上，他不参与web交互功能，主要职责是和服务器交互，和指示缓存的内容。其详细的生命周期和原理文档详见：<a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers">Using Service Workers</a>。可以通过文档中的生命周期对这段后台脚本进行深度开发。</p><p>Hexo 为静态博客，因此只需要实现离线使用即可，不需要进行消息推送，因此可以使用固定服务注册脚本，在 Hexo 中服务注册脚本有着专门的插件进行生成：</p><div class="table-container"><table><thead><tr><th>hexo-offline</th><th>hexo-pwa</th><th>hexo-service-worker</th></tr></thead><tbody><tr><td>Hexo 的离线插件不包括安装</td><td>百度出的PWA综合插件，支持同时生成manifest.json，有很多的配置项</td><td>和hexo-offline类似</td></tr></tbody></table></div><p>三个插件的原理相同，通过注册SW服务，配合<code>manifest.json</code>，文件达到<strong>可安装</strong>和<strong>可离线</strong>的功能，本站使用的是<code>hexo-service-worker</code>插件，下面是插件使用的细节：</p><ol><li>首先安装<code>hexo-service-worker</code>插件：</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">npm install --save hexo-service-worker<br></code></pre></td></tr></table></figure><ol><li>在 Hexo 的全局配置文件<code>config.yml</code>中添加配置</li></ol><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># offline config passed to sw-precache.</span><br><span class="hljs-attr">service_worker:</span><br>  <span class="hljs-attr">maximumFileSizeToCacheInBytes:</span> <span class="hljs-number">5242880</span><br>  <span class="hljs-attr">staticFileGlobs:</span><br>  <span class="hljs-bullet">-</span> <span class="hljs-string">public/index.html</span><br>  <span class="hljs-bullet">-</span> <span class="hljs-string">public/img/favcion.png</span><br>  <span class="hljs-bullet">-</span> <span class="hljs-string">public/manifest.json</span><br>  <span class="hljs-attr">stripPrefix:</span> <span class="hljs-string">public</span><br>  <span class="hljs-attr">verbose:</span> <span class="hljs-literal">false</span><br>  <span class="hljs-attr">runtimeCaching:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-attr">urlPattern:</span> <span class="hljs-string">/**/*</span><br>      <span class="hljs-attr">handler:</span> <span class="hljs-string">cacheFirst</span><br>      <span class="hljs-attr">options:</span><br>        <span class="hljs-attr">origin:</span> <span class="hljs-string">eatrice.top</span><br></code></pre></td></tr></table></figure><p>其中</p><blockquote><ul><li>maximumFileSizeToCacheInBytes 为最大缓存大小，字节数</li><li>staticFileGlobs 关键的文件路径</li><li>stripPrefix 网站文件的根路径绝对位置</li><li>runtimeCaching 缓存选项</li><li>urlPattern 文件的正则匹配</li><li>handler 缓存模式</li><li>origin 网站访问域名(代理域名)</li></ul></blockquote><p>如此支持离线的PWA即配置成功。若要使用其他两个插件进行配置可以参考：</p><ul><li><a href="https://blog.naaln.com/2017/09/hexo-with-pwa/">hexo-offline 插件配置</a></li><li><a href="http://wsyks.github.io/2018/12/17/hexo%E5%8D%9A%E5%AE%A2%E9%85%8D%E7%BD%AEPWA/#%E5%AE%89%E8%A3%85hexo-pwa">hexo-pwa 插件配置</a></li></ul><p>然后执行生成发布。使用新版的chrome访问网站，打开控制台的<code>Audits</code>点击生成报告，就能看到网站是否支持PWA啦，如下图所示：</p><p><img src="https://rmt.dogedoge.com/fetch/fluid/storage/hexo-pwa/2.png?w=1280&amp;fmt=webp" alt=""></p><p>发布之后可以先访问一下网站的一些页面，然后就可以拿把大剪子网线访问你的网站啦~</p><p>关于消息推送，还没太搞明白其中的原理，自己的博客也用不到，所以就不仔细讨论啦。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p>[1]: <a href="https://www.sysgeek.cn/progressive-web-apps/">什么是「渐进式 Web 应用」，PWA 应用程序解析</a></p>]]></content>
    
    
    <categories>
      
      <category>功能增强</category>
      
    </categories>
    
    
    <tags>
      
      <tag>Hexo</tag>
      
      <tag>用户经验</tag>
      
      <tag>花里胡哨</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>Hello Fluid</title>
    <link href="/blog/posts/9604e2c6.html"/>
    <url>/blog/posts/9604e2c6.html</url>
    
    <content type="html"><![CDATA[<blockquote><p>欢迎体验 <a href="https://github.com/fluid-dev/hexo-theme-fluid">Fluid</a> ，这是一款 Material Design 风格的 Hexo 主题，以简约的设计帮助你专注于写作，本篇文章可预览主题的样式及功能。</p></blockquote><span id="more"></span><h2 id="文字"><a href="#文字" class="headerlink" title="文字"></a>文字</h2><p>文章大部分使用的是 github-markdown 样式，并加入了一些 Material 风格。</p><h3 id="H3-标题"><a href="#H3-标题" class="headerlink" title="H3 标题"></a>H3 标题</h3><h4 id="H4-标题"><a href="#H4-标题" class="headerlink" title="H4 标题"></a>H4 标题</h4><p><strong>粗体</strong></p><p><em>斜体</em></p><h2 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h2><p>行内代码：<code>$ hexo new post &quot;My New Post&quot;</code></p><p>代码高亮使用的是 highlight.js，支持 185 种语言和 91 种高亮样式：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">fib</span>(<span class="hljs-params">n</span>):</span><br>    a, b = <span class="hljs-number">0</span>, <span class="hljs-number">1</span><br>    <span class="hljs-keyword">while</span> a &lt; n:<br>        <span class="hljs-built_in">print</span>(a, end=<span class="hljs-string">&#x27; &#x27;</span>)<br>        a, b = b, a+b<br>    <span class="hljs-built_in">print</span>()<br>fib(<span class="hljs-number">1000</span>)<br></code></pre></td></tr></table></figure><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">type</span> Map <span class="hljs-keyword">struct</span> &#123;<br>    mu Mutex<br>    read atomic.Value<br>    dirty <span class="hljs-keyword">map</span>[<span class="hljs-keyword">interface</span>&#123;&#125;]*entry<br>    misses <span class="hljs-keyword">int</span><br>&#125;<br></code></pre></td></tr></table></figure><h2 id="表格"><a href="#表格" class="headerlink" title="表格"></a>表格</h2><div class="table-container"><table><thead><tr><th>Header 1</th><th>Header 2</th><th>Header 3</th></tr></thead><tbody><tr><td>Key 1</td><td>Value 1</td><td>Comment 1</td></tr><tr><td>Key 2</td><td>Value 2</td><td>Comment 2</td></tr><tr><td>Key 3</td><td>Value 3</td><td>Comment 3</td></tr></tbody></table></div><h2 id="列表"><a href="#列表" class="headerlink" title="列表"></a>列表</h2><h3 id="有序列表"><a href="#有序列表" class="headerlink" title="有序列表"></a>有序列表</h3><p>Fluid 相较于其他主题的优势：</p><ol><li>设计遵循简洁至上，同时具有轻快的体验，和优雅的颜值；</li><li>提供大量定制化配置项，使每个用户使用该主题都能具有独特的样式；</li><li>响应式页面，适配手机、平板等设备；</li></ol><h3 id="无序列表"><a href="#无序列表" class="headerlink" title="无序列表"></a>无序列表</h3><p>Fluid 功能特性：</p><ul><li>图片懒加载</li><li>自定义代码高亮方案</li><li>内置多语言</li><li>支持多款评论插件</li><li>支持使用数据文件存放配置</li><li>自定义静态资源 CDN</li><li>内置文章搜索</li><li>页脚备案信息</li><li>网页访问统计</li><li>支持 LaTeX 数学公式</li><li>支持 mermaid 流程图</li><li>音乐播放器</li></ul><h2 id="图片"><a href="#图片" class="headerlink" title="图片"></a>图片</h2><p><img src="https://rmt.dogedoge.com/fetch/fluid/storage/post.png?w=1280&amp;fmt=webp" alt=""></p><h2 id="LaTex"><a href="#LaTex" class="headerlink" title="LaTex"></a>LaTex</h2><p>基于 MathJax 引擎：</p><script type="math/tex; mode=display">\Gamma _ { \epsilon } ( x ) = [ 1- e ^ { - 2\pi \epsilon } ] ^ { 1- x } \prod _ { n = 0} ^ { \infty } \frac { 1- \operatorname{exp} ( - 2\pi \epsilon ( n + 1) ) } { 1- \operatorname{exp} ( - 2\pi \epsilon ( x + n ) ) }</script><script type="math/tex; mode=display">\left( \begin{array} c t ^ { \prime } \\ x ^ { \prime } \\ y ^ { \prime } \\ z ^ { \prime } \end{array} \right) = \left( \begin{array} { c c c c } { \gamma } & { - \gamma \beta } & { 0 } & { 0 } \\ { - \gamma \beta } & { \gamma } & { 0 } & { 0 } \\ { 0 } & { 0 } & { 1 } & { 0 } \\ { 0 } & { 0 } & { 0 } & { 1 } \end{array} \right) \left( \begin{array} c t \\ x \\ y \\ z \end{array} \right)</script><script type="math/tex; mode=display">6 \mathrm { CO } _ { 2 } + 6 \mathrm { H } _ { 2 } \mathrm { O } \rightarrow \mathrm { C } _ { 6 } \mathrm { H } _ { 12 } \mathrm { O } _ { 6 } + 6 \mathrm { O } _ { 2 }</script><h2 id="流程图"><a href="#流程图" class="headerlink" title="流程图"></a>流程图</h2><p>基于 mermaid 语法：</p><pre><code class=" mermaid">sequenceDiagramparticipant Aliceparticipant BobAlice-&gt;&gt;John: Hello John, how are you?loop Healthcheck    John-&gt;&gt;John: Fight against hypochondriaendNote right of John: Rational thoughts &lt;br/&gt;prevail...John--&gt;&gt;Alice: Great!John-&gt;&gt;Bob: How about you?Bob--&gt;&gt;John: Jolly good!</code></pre><pre><code class=" mermaid">ganttdateFormat  YYYY-MM-DDtitle Adding GANTT diagram to mermaidsection A sectionCompleted task            :done,    des1, 2014-01-06,2014-01-08Active task               :active,  des2, 2014-01-09, 3dFuture task               :         des3, after des2, 5dFuture task2               :         des4, after des3, 5d</code></pre><pre><code class=" mermaid">classDiagramClass01 &lt;|-- AveryLongClass : CoolClass03 *-- Class04Class05 o-- Class06Class07 .. Class08Class09 --&gt; C2 : Where am i?Class09 --* C3Class09 --|&gt; Class07Class07 : equals()Class07 : Object[] elementDataClass01 : size()Class01 : int chimpClass01 : int gorillaClass08 &lt;--&gt; C2: Cool label</code></pre><h2 id="内置-Tag-插件"><a href="#内置-Tag-插件" class="headerlink" title="内置 Tag 插件"></a>内置 Tag 插件</h2><p>内置了一些 Tag 插件，用于实现 Markdown 不容易生成的样式，具体使用方式请见 <a href="https://hexo.fluid-dev.com/docs/guide/#tag-%E6%8F%92%E4%BB%B6">用户指南</a>。</p><h3 id="便签"><a href="#便签" class="headerlink" title="便签"></a>便签</h3><div class="note note-primary">            <p>这里可以写文字 或者 <code>markdown</code></p>           </div><div class="note note-warning">            <p>这里可以写文字 或者 <code>markdown</code></p>           </div><div class="note note-danger">            <p>这里可以写文字 或者 <code>markdown</code></p>           </div><h3 id="行内标签"><a href="#行内标签" class="headerlink" title="行内标签"></a>行内标签</h3><span class="label label-primary">行内标签</span> <span class="label label-warning">行内标签</span> <span class="label label-danger">行内标签</span><h3 id="勾选框"><a href="#勾选框" class="headerlink" title="勾选框"></a>勾选框</h3><div>            <input type="checkbox"  checked="checked">主要是解决一些 Renderer 不支持勾选          </div><h3 id="按钮"><a href="#按钮" class="headerlink" title="按钮"></a>按钮</h3><a class="btn" href="javascript:;"  target="_blank">支持链接</a><h3 id="组图"><a href="#组图" class="headerlink" title="组图"></a>组图</h3><div class="group-image-container"><div class="group-image-row"><div class="group-image-wrap"><img src="https://rmt.dogedoge.com/fetch/fluid/storage/hello-fluid/cover.png?w=480&amp;fmt=webp" alt=""></div><div class="group-image-wrap"><img src="https://rmt.dogedoge.com/fetch/fluid/storage/hello-fluid/cover.png?w=480&amp;fmt=webp" alt=""></div><div class="group-image-wrap"><img src="https://rmt.dogedoge.com/fetch/fluid/storage/hello-fluid/cover.png?w=480&amp;fmt=webp" alt=""></div></div><div class="group-image-row"><div class="group-image-wrap"><img src="https://rmt.dogedoge.com/fetch/fluid/storage/hello-fluid/cover.png?w=480&amp;fmt=webp" alt=""></div><div class="group-image-wrap"><img src="https://rmt.dogedoge.com/fetch/fluid/storage/hello-fluid/cover.png?w=480&amp;fmt=webp" alt=""></div></div></div><h3 id="脚注"><a href="#脚注" class="headerlink" title="脚注"></a>脚注</h3><p>以下是脚注演示<sup id="fnref:1" class="footnote-ref"><a href="#fn:1" rel="footnote"><span class="hint--top hint--rounded" aria-label="脚注演示">[1]</span></a></sup>：</p><p>如果你有 Fluid 主题或 Hexo 博客相关的文章，可以通过 Pull Request 方式投稿<sup id="fnref:2" class="footnote-ref"><a href="#fn:2" rel="footnote"><span class="hint--top hint--rounded" aria-label="投稿具体详见[https://github.com/fluid-dev/hexo-fluid-blog](https://github.com/fluid-dev/hexo-fluid-blog)">[2]</span></a></sup>。</p><section class="footnotes"><div class="footnote-list"><ol><li><span id="fn:1" class="footnote-text"><span>脚注演示<a href="#fnref:1" rev="footnote" class="footnote-backref"> ↩</a></span></span></li><li><span id="fn:2" class="footnote-text"><span>投稿具体详见<a href="https://github.com/fluid-dev/hexo-fluid-blog">https://github.com/fluid-dev/hexo-fluid-blog</a><a href="#fnref:2" rev="footnote" class="footnote-backref"> ↩</a></span></span></li></ol></div></section>]]></content>
    
    
    <categories>
      
      <category>主题示例</category>
      
    </categories>
    
    
    <tags>
      
      <tag>示例</tag>
      
      <tag>Fluid</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>mapboxgl中解决图片动画的性能问题</title>
    <link href="/blog/posts/5796179d.html"/>
    <url>/blog/posts/5796179d.html</url>
    
    <content type="html"><![CDATA[<p><code>mapbox-gl</code>中的动画图片是不断请求服务器得来的，详情请看<a href="https://docs.mapbox.com/mapbox-gl-js/example/animate-images/">地址</a>，虽然是实现了，但一直请求不是好的解决办法，特别还是<code>mapbox-gl</code>这种追求性能极致的来说。我们可以有多种解决方法。</p><h2 id="方案一：使用updateImage来解决"><a href="#方案一：使用updateImage来解决" class="headerlink" title="方案一：使用updateImage来解决"></a>方案一：使用updateImage来解决</h2><p>首先还是加载图层资源。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js">map.addSource(<span class="hljs-string">&#x27;layerSourceName&#x27;</span>, &#123;<br>     <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;image&#x27;</span>,<br>     <span class="hljs-attr">url</span>: imageUrl,<br>     <span class="hljs-attr">coordinates</span>: coor,<br>&#125;);<br></code></pre></td></tr></table></figure><p>然后加载这个资源到图层。</p><p>最重要的来了，定时显示图片的环节。</p><span id="more"></span><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">//构造函数如下：   </span><br><span class="hljs-function"><span class="hljs-title">constructor</span>(<span class="hljs-params">id, option</span>)</span> &#123;<br>        <span class="hljs-keyword">const</span> &#123; distance, level, coordinate, frameCount, urlPrifix &#125; = option;<br>        <span class="hljs-built_in">this</span>._id = id;<br>        <span class="hljs-built_in">this</span>._distance = distance;<br>        <span class="hljs-built_in">this</span>._level = level;<br>        <span class="hljs-built_in">this</span>._coordinate = coordinate;<br>        <span class="hljs-built_in">this</span>._frameCount = frameCount;<br>        <span class="hljs-built_in">this</span>._currentImage = <span class="hljs-built_in">this</span>._getRandomInt(<span class="hljs-number">1</span>, option.frameCount - <span class="hljs-number">1</span>);<br>        <span class="hljs-built_in">this</span>._layerSourceName = id;<br>        <span class="hljs-built_in">this</span>._layerName = id;<br>        <span class="hljs-built_in">this</span>._urlPrifix = urlPrifix;<br>        <span class="hljs-built_in">this</span>._map = <span class="hljs-literal">null</span>;<br>        <span class="hljs-built_in">this</span>._imagesMap = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Map</span>();<br>    &#125;   <br><span class="hljs-comment">// 定时显示图片</span><br><span class="hljs-function"><span class="hljs-title">tick</span>(<span class="hljs-params"></span>)</span> &#123;<br>        <span class="hljs-keyword">let</span> index = <span class="hljs-number">1</span>;<br>        <span class="hljs-keyword">const</span> radarEffect = <span class="hljs-function">() =&gt;</span> &#123;<br>            <span class="hljs-keyword">if</span> (index % <span class="hljs-number">4</span> !== <span class="hljs-number">0</span>) &#123;<br>                index += <span class="hljs-number">1</span>;<br>            &#125; <span class="hljs-keyword">else</span> &#123;<br>                index = <span class="hljs-number">1</span>;<br>                <span class="hljs-keyword">const</span> source = <span class="hljs-built_in">this</span>.map.getSource(<span class="hljs-built_in">this</span>._layerSourceName);<br>                <span class="hljs-built_in">this</span>._currentImage = (<span class="hljs-built_in">this</span>._currentImage + <span class="hljs-number">1</span>) % <span class="hljs-built_in">this</span>._frameCount;<br>                <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>._imagesMap.has(<span class="hljs-built_in">this</span>._currentImage)) &#123;<br>                    source.updateImage(&#123; <span class="hljs-attr">url</span>: <span class="hljs-built_in">this</span>._getPath(<span class="hljs-built_in">this</span>._prefix, <span class="hljs-built_in">this</span>._currentImage) &#125;);<br>                    <span class="hljs-keyword">if</span> (source.image) &#123;<br>                        <span class="hljs-built_in">this</span>._imagesMap.set(<span class="hljs-built_in">this</span>._currentImage, source.image);<br>                    &#125;<br>                &#125; <span class="hljs-keyword">else</span> &#123;<br>                    source.texture = <span class="hljs-literal">null</span>;<br>                    source.image = <span class="hljs-built_in">this</span>._imagesMap.get(<span class="hljs-built_in">this</span>._currentImage);<br>                    source._finishLoading.call(source);<br>                &#125;<br>            &#125;<br>            <span class="hljs-built_in">window</span>.requestAnimationFrame(radarEffect);<br>        &#125;;<br>        <span class="hljs-built_in">window</span>.requestAnimationFrame(radarEffect);<br>    &#125;<br> <span class="hljs-function"><span class="hljs-title">_getPath</span>(<span class="hljs-params"></span>)</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">$&#123;<span class="hljs-built_in">this</span>._urlPrifix&#125;</span>/<span class="hljs-subst">$&#123;<span class="hljs-built_in">this</span>._currentImage&#125;</span>.png`</span>;<br>    &#125;<br></code></pre></td></tr></table></figure><h2 id="方案二：使用精灵图来解决。"><a href="#方案二：使用精灵图来解决。" class="headerlink" title="方案二：使用精灵图来解决。"></a>方案二：使用精灵图来解决。</h2><p>话不多说，talk is cheap ,show my code</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> pulsingDot = &#123;<br>    <span class="hljs-attr">width</span>: size,<br>    <span class="hljs-attr">height</span>: size,<br>    <span class="hljs-attr">data</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Uint8Array</span>(size * size * <span class="hljs-number">4</span>),<br><br>    <span class="hljs-comment">// get rendering context for the map canvas when layer is added to the map</span><br>    <span class="hljs-function"><span class="hljs-title">onAdd</span>(<span class="hljs-params"></span>)</span> &#123;<br>        <span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">&#x27;canvas&#x27;</span>);<br>        canvas.width = <span class="hljs-built_in">this</span>.width;<br>        canvas.height = <span class="hljs-built_in">this</span>.height;<br>        <span class="hljs-built_in">this</span>.context = canvas.getContext(<span class="hljs-string">&#x27;2d&#x27;</span>);<br>        <span class="hljs-keyword">const</span> image = <span class="hljs-keyword">new</span> Image();<br>        image.src = <span class="hljs-string">&#x27;./green.png&#x27;</span>;<br>        <span class="hljs-built_in">this</span>.image = image;<br>        <span class="hljs-comment">// 创建一个精灵图对象</span><br>        <span class="hljs-keyword">const</span> sprite = <span class="hljs-keyword">new</span> Sprite(&#123;<br>            canvas,<br>            image,<br>            <span class="hljs-attr">numberOfFrames</span>: <span class="hljs-number">10</span>,<br>            <span class="hljs-attr">ticksPerFrame</span>: <span class="hljs-number">0</span>,<br>            <span class="hljs-attr">row</span>: <span class="hljs-number">7</span>,<br>            <span class="hljs-attr">column</span>: <span class="hljs-number">7</span>,<br>            <span class="hljs-attr">x</span>: <span class="hljs-number">0</span>,<br>            <span class="hljs-attr">y</span>: <span class="hljs-number">0</span>,<br>        &#125;);<br>        <span class="hljs-built_in">this</span>.sprite = sprite;<br>    &#125;,<br><br>    <span class="hljs-comment">// called once before every frame where the icon will be used</span><br>    <span class="hljs-function"><span class="hljs-title">render</span>(<span class="hljs-params"></span>)</span> &#123;<br>        <span class="hljs-keyword">const</span> context = <span class="hljs-built_in">this</span>.context;<br>        <span class="hljs-comment">// console.log(`rdapp: render -&gt; this`, this);</span><br><br>        <span class="hljs-comment">// draw outer circle</span><br>        context.clearRect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-built_in">this</span>.width, <span class="hljs-built_in">this</span>.height);<br>        <span class="hljs-built_in">this</span>.sprite.update();<br>        <span class="hljs-built_in">this</span>.sprite.render();<br>        <span class="hljs-built_in">this</span>.data = context.getImageData(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-built_in">this</span>.width, <span class="hljs-built_in">this</span>.height).data;<br><br>        <span class="hljs-comment">// continuously repaint the map, resulting in the smooth animation of the dot</span><br>        map.triggerRepaint();<br><br>        <span class="hljs-comment">// return `true` to let the map know that the image was updated</span><br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>    &#125;,<br>&#125;;<br>map.on(<span class="hljs-string">&#x27;load&#x27;</span>, <span class="hljs-function">() =&gt;</span> &#123;<br>    map.addImage(<span class="hljs-string">&#x27;pulsing-dot&#x27;</span>, pulsingDot, &#123; <span class="hljs-attr">pixelRatio</span>: <span class="hljs-number">2</span> &#125;);<br><br>    map.addSource(<span class="hljs-string">&#x27;points&#x27;</span>, &#123;<br>        <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;geojson&#x27;</span>,<br>        <span class="hljs-attr">data</span>: &#123;<br>            <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;FeatureCollection&#x27;</span>,<br>            <span class="hljs-attr">features</span>: [<br>                &#123;<br>                    <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;Feature&#x27;</span>,<br>                    <span class="hljs-attr">geometry</span>: &#123;<br>                        <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;Point&#x27;</span>,<br>                        <span class="hljs-attr">coordinates</span>: mapConfig.center,<br>                    &#125;,<br>                &#125;,<br>            ],<br>        &#125;,<br>    &#125;);<br>    map.addLayer(&#123;<br>        <span class="hljs-attr">id</span>: <span class="hljs-string">&#x27;points&#x27;</span>,<br>        <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;symbol&#x27;</span>,<br>        <span class="hljs-attr">source</span>: <span class="hljs-string">&#x27;points&#x27;</span>,<br>        <span class="hljs-attr">layout</span>: &#123;<br>            <span class="hljs-string">&#x27;icon-image&#x27;</span>: <span class="hljs-string">&#x27;pulsing-dot&#x27;</span>,<br>            <span class="hljs-string">&#x27;icon-rotation-alignment&#x27;</span>: <span class="hljs-string">&#x27;map&#x27;</span>,<br>        &#125;,<br>    &#125;);<br>&#125;);<br></code></pre></td></tr></table></figure>]]></content>
    
    
    
    <tags>
      
      <tag>Mapboxgl</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>mapboxgl中解决图片动画的性能问题</title>
    <link href="/blog/posts/5796179d.html"/>
    <url>/blog/posts/5796179d.html</url>
    
    <content type="html"><![CDATA[<p><code>mapbox-gl</code>中的动画图片是不断请求服务器得来的，详情请看<a href="https://docs.mapbox.com/mapbox-gl-js/example/animate-images/">地址</a>，虽然是实现了，但一直请求不是好的解决办法，特别还是<code>mapbox-gl</code>这种追求性能极致的来说。我们可以有多种解决方法。</p><h2 id="方案一：使用updateImage来解决"><a href="#方案一：使用updateImage来解决" class="headerlink" title="方案一：使用updateImage来解决"></a>方案一：使用updateImage来解决</h2><p>首先还是加载图层资源。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js">map.addSource(<span class="hljs-string">&#x27;layerSourceName&#x27;</span>, &#123;<br>     <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;image&#x27;</span>,<br>     <span class="hljs-attr">url</span>: imageUrl,<br>     <span class="hljs-attr">coordinates</span>: coor, <span class="hljs-comment">//计算后的四至坐标</span><br>&#125;);<br></code></pre></td></tr></table></figure><p>然后加载这个资源到图层。</p><p>最重要的来了，定时显示图片的环节。</p><span id="more"></span><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">//构造函数如下：   </span><br><span class="hljs-function"><span class="hljs-title">constructor</span>(<span class="hljs-params">id, option</span>)</span> &#123;<br>        <span class="hljs-keyword">const</span> &#123; distance, level, coordinate, frameCount, urlPrifix &#125; = option;<br>        <span class="hljs-built_in">this</span>._id = id;<br>        <span class="hljs-built_in">this</span>._distance = distance;<br>        <span class="hljs-built_in">this</span>._level = level;<br>        <span class="hljs-built_in">this</span>._coordinate = coordinate;<br>        <span class="hljs-built_in">this</span>._frameCount = frameCount;<br>        <span class="hljs-built_in">this</span>._currentImage = <span class="hljs-built_in">this</span>._getRandomInt(<span class="hljs-number">1</span>, option.frameCount - <span class="hljs-number">1</span>);<br>        <span class="hljs-built_in">this</span>._layerSourceName = id;<br>        <span class="hljs-built_in">this</span>._layerName = id;<br>        <span class="hljs-built_in">this</span>._urlPrifix = urlPrifix;<br>        <span class="hljs-built_in">this</span>._map = <span class="hljs-literal">null</span>;<br>        <span class="hljs-built_in">this</span>._imagesMap = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Map</span>();<br>    &#125;   <br><span class="hljs-comment">// 定时显示图片</span><br><span class="hljs-function"><span class="hljs-title">tick</span>(<span class="hljs-params"></span>)</span> &#123;<br>        <span class="hljs-keyword">let</span> index = <span class="hljs-number">1</span>;<br>        <span class="hljs-keyword">const</span> radarEffect = <span class="hljs-function">() =&gt;</span> &#123;<br>            <span class="hljs-keyword">if</span> (index % <span class="hljs-number">4</span> !== <span class="hljs-number">0</span>) &#123;<br>                index += <span class="hljs-number">1</span>;<br>            &#125; <span class="hljs-keyword">else</span> &#123;<br>                index = <span class="hljs-number">1</span>;<br>                <span class="hljs-keyword">const</span> source = <span class="hljs-built_in">this</span>.map.getSource(<span class="hljs-built_in">this</span>._layerSourceName);<br>                <span class="hljs-built_in">this</span>._currentImage = (<span class="hljs-built_in">this</span>._currentImage + <span class="hljs-number">1</span>) % <span class="hljs-built_in">this</span>._frameCount;<br>                <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>._imagesMap.has(<span class="hljs-built_in">this</span>._currentImage)) &#123;<br>                    source.updateImage(&#123; <span class="hljs-attr">url</span>: <span class="hljs-built_in">this</span>._getPath(<span class="hljs-built_in">this</span>._prefix, <span class="hljs-built_in">this</span>._currentImage) &#125;);<br>                    <span class="hljs-keyword">if</span> (source.image) &#123;<br>                        <span class="hljs-built_in">this</span>._imagesMap.set(<span class="hljs-built_in">this</span>._currentImage, source.image);<br>                    &#125;<br>                &#125; <span class="hljs-keyword">else</span> &#123;<br>                    source.texture = <span class="hljs-literal">null</span>;<br>                    source.image = <span class="hljs-built_in">this</span>._imagesMap.get(<span class="hljs-built_in">this</span>._currentImage);<br>                    source._finishLoading.call(source);<br>                &#125;<br>            &#125;<br>            <span class="hljs-built_in">window</span>.requestAnimationFrame(radarEffect);<br>        &#125;;<br>        <span class="hljs-built_in">window</span>.requestAnimationFrame(radarEffect);<br>    &#125;<br> <span class="hljs-function"><span class="hljs-title">_getPath</span>(<span class="hljs-params"></span>)</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">$&#123;<span class="hljs-built_in">this</span>._urlPrifix&#125;</span>/<span class="hljs-subst">$&#123;<span class="hljs-built_in">this</span>._currentImage&#125;</span>.png`</span>;<br>    &#125;<br></code></pre></td></tr></table></figure><h2 id="方案二：使用精灵图来解决。"><a href="#方案二：使用精灵图来解决。" class="headerlink" title="方案二：使用精灵图来解决。"></a>方案二：使用精灵图来解决。</h2><p>话不多说，talk is cheap ,show my code</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> pulsingDot = &#123;<br>    <span class="hljs-attr">width</span>: size,<br>    <span class="hljs-attr">height</span>: size,<br>    <span class="hljs-attr">data</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Uint8Array</span>(size * size * <span class="hljs-number">4</span>),<br><br>    <span class="hljs-comment">// get rendering context for the map canvas when layer is added to the map</span><br>    <span class="hljs-function"><span class="hljs-title">onAdd</span>(<span class="hljs-params"></span>)</span> &#123;<br>        <span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">&#x27;canvas&#x27;</span>);<br>        canvas.width = <span class="hljs-built_in">this</span>.width;<br>        canvas.height = <span class="hljs-built_in">this</span>.height;<br>        <span class="hljs-built_in">this</span>.context = canvas.getContext(<span class="hljs-string">&#x27;2d&#x27;</span>);<br>        <span class="hljs-keyword">const</span> image = <span class="hljs-keyword">new</span> Image();<br>        image.src = <span class="hljs-string">&#x27;./green.png&#x27;</span>;<br>        <span class="hljs-built_in">this</span>.image = image;<br>        <span class="hljs-comment">// 创建一个精灵图对象</span><br>        <span class="hljs-keyword">const</span> sprite = <span class="hljs-keyword">new</span> Sprite(&#123;<br>            canvas,<br>            image,<br>            <span class="hljs-attr">numberOfFrames</span>: <span class="hljs-number">10</span>,<br>            <span class="hljs-attr">ticksPerFrame</span>: <span class="hljs-number">0</span>,<br>            <span class="hljs-attr">row</span>: <span class="hljs-number">7</span>,<br>            <span class="hljs-attr">column</span>: <span class="hljs-number">7</span>,<br>            <span class="hljs-attr">x</span>: <span class="hljs-number">0</span>,<br>            <span class="hljs-attr">y</span>: <span class="hljs-number">0</span>,<br>        &#125;);<br>        <span class="hljs-built_in">this</span>.sprite = sprite;<br>    &#125;,<br><br>    <span class="hljs-comment">// called once before every frame where the icon will be used</span><br>    <span class="hljs-function"><span class="hljs-title">render</span>(<span class="hljs-params"></span>)</span> &#123;<br>        <span class="hljs-keyword">const</span> context = <span class="hljs-built_in">this</span>.context;<br>        <span class="hljs-comment">// console.log(`rdapp: render -&gt; this`, this);</span><br><br>        <span class="hljs-comment">// draw outer circle</span><br>        context.clearRect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-built_in">this</span>.width, <span class="hljs-built_in">this</span>.height);<br>        <span class="hljs-built_in">this</span>.sprite.update();<br>        <span class="hljs-built_in">this</span>.sprite.render();<br>        <span class="hljs-built_in">this</span>.data = context.getImageData(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-built_in">this</span>.width, <span class="hljs-built_in">this</span>.height).data;<br><br>        <span class="hljs-comment">// continuously repaint the map, resulting in the smooth animation of the dot</span><br>        map.triggerRepaint();<br><br>        <span class="hljs-comment">// return `true` to let the map know that the image was updated</span><br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>    &#125;,<br>&#125;;<br>map.on(<span class="hljs-string">&#x27;load&#x27;</span>, <span class="hljs-function">() =&gt;</span> &#123;<br>    map.addImage(<span class="hljs-string">&#x27;pulsing-dot&#x27;</span>, pulsingDot, &#123; <span class="hljs-attr">pixelRatio</span>: <span class="hljs-number">2</span> &#125;);<br><br>    map.addSource(<span class="hljs-string">&#x27;points&#x27;</span>, &#123;<br>        <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;geojson&#x27;</span>,<br>        <span class="hljs-attr">data</span>: &#123;<br>            <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;FeatureCollection&#x27;</span>,<br>            <span class="hljs-attr">features</span>: [<br>                &#123;<br>                    <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;Feature&#x27;</span>,<br>                    <span class="hljs-attr">geometry</span>: &#123;<br>                        <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;Point&#x27;</span>,<br>                        <span class="hljs-attr">coordinates</span>: mapConfig.center,<br>                    &#125;,<br>                &#125;,<br>            ],<br>        &#125;,<br>    &#125;);<br>    map.addLayer(&#123;<br>        <span class="hljs-attr">id</span>: <span class="hljs-string">&#x27;points&#x27;</span>,<br>        <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;symbol&#x27;</span>,<br>        <span class="hljs-attr">source</span>: <span class="hljs-string">&#x27;points&#x27;</span>,<br>        <span class="hljs-attr">layout</span>: &#123;<br>            <span class="hljs-string">&#x27;icon-image&#x27;</span>: <span class="hljs-string">&#x27;pulsing-dot&#x27;</span>,<br>            <span class="hljs-string">&#x27;icon-rotation-alignment&#x27;</span>: <span class="hljs-string">&#x27;map&#x27;</span>,<br>        &#125;,<br>    &#125;);<br>&#125;);<br></code></pre></td></tr></table></figure>]]></content>
    
    
    
    <tags>
      
      <tag>Mapboxgl</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>win系统下使用vscode中的remote-SSH插件连接就出错</title>
    <link href="/blog/posts/fa7b679e.html"/>
    <url>/blog/posts/fa7b679e.html</url>
    
    <content type="html"><![CDATA[<p>有了自己的云服务器，就想把自己的网站往上迁移。听闻 vscode 中<code>remote-SSH</code>的插件很好用，于是安装使用一下，木有想到的是，第一步就出错了——连接不上，报<code>Bad owner or permissions</code>的错！！</p><img src="/blog/posts/fa7b679e/image-20200325224544283.png" class="" title="image-20200325224544283"><span id="more"></span><p>网上试了很多种，方法，都没有解决，包括<a href="https://blog.csdn.net/chaoenhu/article/details/103698804?depth_1-utm_source=distribute.pc_relevant.none-task&amp;utm_source=distribute.pc_relevant.none-task">禁用继承</a>、<a href="https://github.com/PowerShell/openssh-portable"><strong>openssh-portable</strong></a> 。但是还是没有解决我的问题，但是在公司的电脑一下子就能连上。我就想着其中的区别，发现其中有点不同，结合上面的禁用继承的文章，于是把第一个用户名删了，我登录的是<code>HelloWorld</code>这个用户。</p><img src="/blog/posts/fa7b679e/image-20200325225154805.png" class="" title="image-20200325225154805"><p>删完的图如下：</p><img src="/blog/posts/fa7b679e/image-20200325225606279.png" class="" title="image-20200325225606279"><p>结果能登录上去了，如图：</p><img src="/blog/posts/fa7b679e/image-20200325225717977.png" class="" title="image-20200325225717977"><blockquote><p>另外可以参考<a href="https://zhuanlan.zhihu.com/p/143146239">个人电脑配置免密登陆SSH</a></p></blockquote>]]></content>
    
    
    
    <tags>
      
      <tag>Vscode</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>在centos上搭建git服务器并自动同步代码</title>
    <link href="/blog/posts/cc12ec28.html"/>
    <url>/blog/posts/cc12ec28.html</url>
    
    <content type="html"><![CDATA[<blockquote><p>本篇内容用来讲述如何将 hexo 博客部署到云服务器上。<br>只要通过三步即可成功部署：</p><ol><li>云服务器端 git 的配置</li><li>Nginx 的配置</li><li>本地端 hexo 的设置更改</li></ol></blockquote><span id="more"></span><h1 id="云服务器端配置-git"><a href="#云服务器端配置-git" class="headerlink" title="云服务器端配置 git"></a>云服务器端配置 git</h1><div class="note danger">可以直接使用 yum install git 安装，也可以使用编译安装，如下：</div><ol><li><p>安装依赖库和编译工具</p><ul><li><p>安装依赖库：</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cmake">yum <span class="hljs-keyword">install</span> curl-devel expat-devel gettext-devel openssl-devel zlib-devel<br></code></pre></td></tr></table></figure></li><li><p>安装编译工具：</p><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs mipsasm">yum <span class="hljs-keyword">install </span>gcc perl-<span class="hljs-keyword">ExtUtils-MakeMaker </span>package<br></code></pre></td></tr></table></figure></li></ul></li><li><p>下载 git</p><ul><li><p>选择一个目录来存放下载下来的 git 安装包。这里选择了<code>/usr/local/src</code> 目录</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs awk">cd <span class="hljs-regexp">/usr/</span>local/src<br></code></pre></td></tr></table></figure></li><li><p>到官网找一个新版稳定的源码包下载到 <code>/usr/local/src</code> 文件夹里</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs awk">wget https:<span class="hljs-regexp">//</span>www.kernel.org<span class="hljs-regexp">/pub/</span>software<span class="hljs-regexp">/scm/gi</span>t/git-<span class="hljs-number">2.16</span>.<span class="hljs-number">2</span>.tar.gz<br></code></pre></td></tr></table></figure></li></ul></li><li><p>解压编译 git</p><ul><li><p>在当前目录下解压 <code>git-2.16.2.tar.gz</code></p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">tar</span> -zvxf git-<span class="hljs-number">2</span>.<span class="hljs-number">16</span>.<span class="hljs-number">2</span>.tar.gz<br></code></pre></td></tr></table></figure></li><li><p>进入 <code>git-2.16.2.tar.gz</code> 目录下</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">cd</span> git-<span class="hljs-number">2</span>.<span class="hljs-number">16</span>.<span class="hljs-number">2</span><br></code></pre></td></tr></table></figure></li><li><p>执行编译</p><figure class="highlight arcade"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs arcade">make all prefix=<span class="hljs-regexp">/usr/</span>local/git<br></code></pre></td></tr></table></figure></li><li><p>安装 git 到 <code>/usr/local/git</code> 目录下</p><figure class="highlight arcade"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs arcade">make install prefix=<span class="hljs-regexp">/usr/</span>local/git<br></code></pre></td></tr></table></figure></li></ul></li><li><p>配置 git 环境变量</p><ul><li><p>将 git 加入 PATH 目录中</p><figure class="highlight arcade"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs arcade">echo <span class="hljs-string">&#x27;export PATH=$PATH:/usr/local/git/bin&#x27;</span> &gt;&gt; <span class="hljs-regexp">/etc/</span>bashrc<br></code></pre></td></tr></table></figure></li><li><p>使 git 环境变量生效</p><figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs gradle"><span class="hljs-keyword">source</span> <span class="hljs-regexp">/etc/</span>bashrc<br></code></pre></td></tr></table></figure></li></ul></li><li><p>查看 git 版本</p><figure class="highlight ada"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ada">git <span class="hljs-comment">--version</span><br></code></pre></td></tr></table></figure></li></ol><p>如果此时能查看到 git 的版本号，说明我们已经安装成功了。</p><ol><li><p>创建 git 仓库，用于存放博客网站资源。</p><p>在 <code>home/git</code> 的目录下，创建一个名为<code>hexoBlog</code>的裸仓库（bare repo）。</p><p> 如果没有 <code>home/git</code> 目录，需要先创建；然后修改目录的所有权和用户权限。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs shell">mkdir /home/git/<br>chown -R $USER:$USER /home/git/<br>chmod -R 755 /home/git/<br></code></pre></td></tr></table></figure><p> 然后，执行如下命令：</p></li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">cd /home/git/<br>git init --bare aigisss.git<br></code></pre></td></tr></table></figure><p>刚才这一步主要创建一个裸的 git 仓库。</p><ol><li><p>创建一个新的 git 钩子，用于自动部署。</p><ol><li><p>在 <code>/home/git/aigisss.git</code> 下，有一个自动生成的 <code>hooks</code> 文件夹。我们需要在里边新建一个新的钩子文件 <code>post-receive</code>。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">vim /home/git/aigisss.git/hooks/post-receive<br></code></pre></td></tr></table></figure></li><li><p>按 <code>i</code> 键进入文件的编辑模式，在该文件中添加两行代码（将下边的代码粘贴进去)，指定 Git 的工作树（源代码）和 Git 目录（配置文件等）</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell"><span class="hljs-meta">#</span><span class="bash">!/bin/bash</span><br>git --work-tree=/home/web/aigisss --git-dir=/home/git/aigisss.git checkout -f<br></code></pre></td></tr></table></figure><p>然后，按 <code>Esc</code> 键退出编辑模式，输入<code>:wq</code> 保存退出。</p></li><li><p>修改文件权限，使得其可执行</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">chmod +x /home/git/aigisss.git/hooks/post-receive<br></code></pre></td></tr></table></figure></li></ol></li></ol><p>到这里，我们的 git 仓库算是完全搭建好了。下面进行 <code>Nginx</code>的配置。</p><h1 id="云服务器端配置-Nginx"><a href="#云服务器端配置-Nginx" class="headerlink" title="云服务器端配置 Nginx"></a>云服务器端配置 Nginx</h1><ol><li><p>安装 Nginx</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cmake">yum <span class="hljs-keyword">install</span> -y nginx<br></code></pre></td></tr></table></figure></li><li><p>启动 Nginx</p><figure class="highlight crmsh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs crmsh">service nginx <span class="hljs-literal">start</span><br></code></pre></td></tr></table></figure></li><li><p>测试 Nginx 服务器</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs awk">wget https:<span class="hljs-regexp">//</span><span class="hljs-number">127.0</span>.<span class="hljs-number">0.1</span><br></code></pre></td></tr></table></figure></li></ol><p>能够正常获取欢迎页面说明Nginx安装成功。</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">Connecting</span> to <span class="hljs-number">127.0.0.1:80</span>... connected.<br><span class="hljs-attribute">HTTP</span> request sent, awaiting response... <span class="hljs-number">200</span> OK<br><span class="hljs-attribute">Length</span>: <span class="hljs-number">43704</span> (<span class="hljs-number">43</span>K)<span class="hljs-meta"> [text/html]</span><br><span class="hljs-attribute">Saving</span> to: ‘index.html’<br><br><span class="hljs-attribute">100</span>%[=======================================&gt;] <span class="hljs-number">43</span>,<span class="hljs-number">704</span>      --.-K/s   in <span class="hljs-number">0</span>s<br><br><span class="hljs-attribute">2018</span>-<span class="hljs-number">03</span>-<span class="hljs-number">09</span> <span class="hljs-number">23</span>:<span class="hljs-number">04</span>:<span class="hljs-number">09</span> (<span class="hljs-number">487</span> MB/s) - ‘index.html’ saved<span class="hljs-meta"> [43704/43704]</span><br></code></pre></td></tr></table></figure><ol><li><p>测试网页是否能打开<br>在浏览器中输入服务器 ip 地址，就是服务器的公网 ip。</p></li><li><p>配置 <code>Nginx</code> 托管文件目录</p><ol><li><p>接下来，创建 <code>/home/hexoBlog</code>目录，用于 <code>Nginx</code>托管。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs shell">mkdir /home/web/aigisss/<br>chown -R $USER:$USER /home/web/aigisss/<br>chmod -R 755 /home/web/aigisss/<br></code></pre></td></tr></table></figure></li><li><p>查看 <code>Nginx</code> 的默认配置的安装位置</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ebnf"><span class="hljs-attribute">nginx -t</span><br></code></pre></td></tr></table></figure></li><li><p>修改<code>Nginx</code>的默认配置，其中 <code>cd</code> 后边就是刚才查到的安装位置（每个人可能都不一样）</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">vim /etc/nginx/nginx.conf<br></code></pre></td></tr></table></figure></li><li><p>按方向键，找到如下位置</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs nginx"><span class="hljs-section">server</span> &#123;<br>    <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span> default_server;<br>    <span class="hljs-attribute">listen</span> [::]:<span class="hljs-number">80</span> default_server;<br>    <span class="hljs-attribute">root</span> /home/hexoBlog;    <span class="hljs-comment">#需要修改</span><br>    <br>    <span class="hljs-attribute">server_name</span> www.bujige.net; <span class="hljs-comment">#需要修改</span><br>    <br>    <span class="hljs-comment"># Load configuration files for the default server block.</span><br>    <span class="hljs-attribute">include</span> /etc/nginx/default.d/<span class="hljs-regexp">*.conf</span>;<br>    <span class="hljs-attribute">location</span> / &#123;<br>    &#125;<br>    <span class="hljs-attribute">error_page</span> <span class="hljs-number">404</span> /<span class="hljs-number">404</span>.html;<br>        <span class="hljs-attribute">location</span> = /40x.html &#123;<br>    &#125;<br></code></pre></td></tr></table></figure><p>按<code>i</code>键进入插入模式，将其中的 root 值改为 <code>/home/hexoBlog</code> （刚才创建的托管仓库目录）。<br>将 server_name 值改成你的域名。</p></li><li><p>重启 <code>Nginx</code> 服务</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ebnf"><span class="hljs-attribute">service nginx restart</span><br></code></pre></td></tr></table></figure></li></ol></li></ol><p>至此，服务器端配置就结束了。接下来，就剩下本地 <code>hexo</code> 的配置更改了。</p><h1 id="修改-hexo-站点配置文件-git-相关设置"><a href="#修改-hexo-站点配置文件-git-相关设置" class="headerlink" title="修改 hexo 站点配置文件 git 相关设置"></a>修改 hexo 站点配置文件 git 相关设置</h1><ol><li><p>打开你本地的 hexo 博客所在文件，打开站点配置文件（不是主题配置文件），做以下修改。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs shell">deploy:<br>    type: git<br>    repo: root@CVM 你的云服务器的IP地址:/home/git/aigisss<br>    branch: master<br></code></pre></td></tr></table></figure></li><li><p>在 hexo 目录下执行部署，试试看。</p><figure class="highlight verilog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs verilog">cd 你的 hexo 目录<br>hexo clean<br>hexo <span class="hljs-keyword">generate</span><br>hexo deploy<br></code></pre></td></tr></table></figure></li><li><p>打开你的公网 IP，看是不是已经部署成功了。</p></li></ol><p><a href="http://qncdn.bujige.net/images/hexoBlog-deployed-server-005.png"><img src="%E5%9C%A8centos%E4%B8%8A%E6%90%AD%E5%BB%BAgit%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%B9%B6%E8%87%AA%E5%8A%A8%E5%90%8C%E6%AD%A5%E4%BB%A3%E7%A0%81/hexoBlog-deployed-server-005.png" alt="img"></a></p><ol><li><p>最后一步，更改域名解析。这一步不再做介绍。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sh">cert --nginx --nginx-server-root=/usr/<span class="hljs-built_in">local</span>/nginx/conf -d www.gishai.top<br></code></pre></td></tr></table></figure></li></ol>]]></content>
    
    
    
    <tags>
      
      <tag>建站</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>vue-cli-service-build报错</title>
    <link href="/blog/posts/26a3b371.html"/>
    <url>/blog/posts/26a3b371.html</url>
    
    <content type="html"><![CDATA[<p><code>vue-cli-service-build报错No module factory available for dependency type: CssDependency</code></p><p><a href="https://forum.vuejs.org/t/vue-cli-service-build/83951">https://forum.vuejs.org/t/vue-cli-service-build/83951</a></p><p>在<code>vue.config.js</code>文件中配置</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-built_in">module</span>.exports = &#123;<br>  <span class="hljs-attr">css</span>: &#123;<br>    <span class="hljs-attr">extract</span>: <span class="hljs-literal">false</span><br>  &#125;<br>&#125;;<br></code></pre></td></tr></table></figure><p>原因看下面<br><a href="https://cli.vuejs.org/config/#css-extract">https://cli.vuejs.org/config/#css-extract</a><br><a href="https://segmentfault.com/a/1190000016390112">https://segmentfault.com/a/1190000016390112</a></p>]]></content>
    
    
    
    <tags>
      
      <tag>Vue</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>gitlab-ci自动化部署</title>
    <link href="/blog/posts/9604e2a7.html"/>
    <url>/blog/posts/9604e2a7.html</url>
    
    <content type="html"><![CDATA[<img src="/blog/posts/9604e2a7/48872323f3f04c3e99e0d2f1ff5f329e_th.jpg" class=""><span id="more"></span><h2 id="前言："><a href="#前言：" class="headerlink" title="前言："></a>前言：</h2><h3 id="此文章是公司同事培训时准备的资料"><a href="#此文章是公司同事培训时准备的资料" class="headerlink" title="此文章是公司同事培训时准备的资料"></a>此文章是公司同事培训时准备的资料</h3><p>这里先为大家展示下 gitlab 与 gitlab-ci 的工作流程图，如下</p><img src="/blog/posts/9604e2a7/0gitlab-ci-01.png" class="" title="0gitlab-ci流程图"><h2 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h2><h3 id="打包部署在同一虚拟机"><a href="#打包部署在同一虚拟机" class="headerlink" title="打包部署在同一虚拟机"></a>打包部署在同一虚拟机</h3><blockquote><p>1，虚拟机需要安装 gitlab-runner 服务；</p><p>2，安装 node 及 git 环境（如果 node 依赖有用到私仓，需要添加切换私仓镜像）</p></blockquote><h3 id="打包部署在不同虚拟机"><a href="#打包部署在不同虚拟机" class="headerlink" title="打包部署在不同虚拟机"></a>打包部署在不同虚拟机</h3><blockquote><p>1，打包虚拟机安装内容同 1.1；</p><p>2，部署虚拟机仅需安装 gitlab-runner 服务即可</p></blockquote><h3 id="gitlab-runner-安装步骤"><a href="#gitlab-runner-安装步骤" class="headerlink" title="gitlab-runner 安装步骤"></a>gitlab-runner 安装步骤</h3><blockquote><p>1，在服务器 C 盘或其他盘符新建一个 GitlabRunner 的文件夹，将文档根目录下的<code>gitlab-runner-windows-amd64.exe</code>程序拷贝到其中</p><p>2，在 GitlabRunner 文件夹目录下 shift+右键，“在此处打开命令窗口”</p><p>3，输入 <code>gitlab-runner-windows-amd64.exe register</code> 根据提示填写内容（如下图）</p><img src="/blog/posts/9604e2a7/1gitlab-runner%E5%AE%89%E8%A3%85%E9%85%8D%E7%BD%AE.png" class="" title="1gitlab-runner安装配置"><p>其中 token 是通过管理员登陆 gitlab 后，在 Runners 中复制过来，如下图</p><img src="/blog/posts/9604e2a7/2gitlab-runner-token%E9%85%8D%E7%BD%AE.png" class="" title="2gitlab-runner-token配置"><p><strong>注意：tags 的配置可以配置多个，命令行中可以按照 description 的格式来写，后面 yml 文件中会用到</strong></p><p>4，输入 <code>gitlab-runner-windows-amd64.exe install</code> 没有报错后再输入 <code>gitlab-runner-windows-amd64.exe start</code></p><p>5，打开任务管理器查看 gitlab-runner 是否正在运行，如下图</p><img src="/blog/posts/9604e2a7/3gitlab-runner%E8%BF%9B%E7%A8%8B.png" class="" title="3gitlab-runner进程"><p>6，最终自己登陆上 gitlab 后可以在 看到刚添加上的 gitlab-runner 的状态，如下图状态即为安装成功</p><img src="/blog/posts/9604e2a7/4gitlab-runner%E7%8A%B6%E6%80%81.png" class="" title="4gitlab-runner状态"></blockquote><h2 id="部署流程"><a href="#部署流程" class="headerlink" title="部署流程"></a>部署流程</h2><h3 id="部署服务器上配置开启-web-server"><a href="#部署服务器上配置开启-web-server" class="headerlink" title="部署服务器上配置开启 web-server"></a>部署服务器上配置开启 web-server</h3><blockquote><p>常用 web-server 有 IIS 和 nginx，配置好后，开启 web 服务后，将存放 web 页面的文件路径记录好，如下</p><p><code>E:\web\autodeploy\</code> 后面 yml 配置文件要用到</p></blockquote><h3 id="gitlab-建立仓储或者使用已有的仓储"><a href="#gitlab-建立仓储或者使用已有的仓储" class="headerlink" title="gitlab 建立仓储或者使用已有的仓储"></a>gitlab 建立仓储或者使用已有的仓储</h3><blockquote><p>这里已 vue 项目为例</p><p>1，本地打开项目代码，新建一个名为 <code>.gitlab-ci.yml</code> 文件在项目根目录（与 package.json 同级）</p><p>2，<code>.gitlab-ci.yml</code> 中添加配置，详见三</p><p>3，<code>git push origin master</code> push 完成后，在 gitlab 仓储的 CI/CD 中的 Pipeline 中就可以看到部署进度（部署的时长与 npm install 的速度相关）所以建议减少高频 push 代码来触发自动化部署或者通过指定分支来做（在.gitlab-ci.yml 中解释如何操作）</p><p>4，push 到远端后，就可以在仓储 CI/CD 的 Pipelines 中看到这条流水线（如下图状态）</p><img src="/blog/posts/9604e2a7/1574913007541.png" class="" width="1574913007541"><p>5，也可以点开上图红框按钮，进入到当前 job 中查看部署日志状态（如下图），完成后日志结尾会提示 <code>Job succeeded</code></p><img src="/blog/posts/9604e2a7/1574913039962.png" class="" width="1574913039962"><p>6，完成后，如下图状态，即为部署成功，这是就可以打开自己在 web-server 上配置的 ip 或者域名来访问网站（此处样例链接 <a href="http://autodeploy.rdapp/">http://autodeploy.rdapp/</a> ）</p><img src="/blog/posts/9604e2a7/5gitlab-runner%E9%83%A8%E7%BD%B2%E6%88%90%E5%8A%9F.png" class="" title="5gitlab-runner部署成功"></blockquote><h2 id="gitlab-ci-yml-文件配置解释"><a href="#gitlab-ci-yml-文件配置解释" class="headerlink" title=".gitlab-ci.yml 文件配置解释"></a>.gitlab-ci.yml 文件配置解释</h2><h3 id="gitlab-ci-yml-每步配置的解释，见下图："><a href="#gitlab-ci-yml-每步配置的解释，见下图：" class="headerlink" title=".gitlab-ci.yml 每步配置的解释，见下图："></a>.gitlab-ci.yml 每步配置的解释，见下图：</h3><img src="/blog/posts/9604e2a7/6gitlab-ci%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6.png" class="" title="6gitlab-ci配置文件"><h3 id="gitlab-ci-yml-源码，可直接复制，针对项目部署情况修改使用"><a href="#gitlab-ci-yml-源码，可直接复制，针对项目部署情况修改使用" class="headerlink" title=".gitlab-ci.yml 源码，可直接复制，针对项目部署情况修改使用"></a>.gitlab-ci.yml 源码，可直接复制，针对项目部署情况修改使用</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">stages:</span><br>  <span class="hljs-bullet">-</span> <span class="hljs-string">build</span><br>  <span class="hljs-bullet">-</span> <span class="hljs-string">deploy</span><br><br><span class="hljs-comment"># cache:</span><br><span class="hljs-comment">#   key: &quot;$CI_BUILD_REF_NAME&quot;</span><br><span class="hljs-comment">#   paths:</span><br><span class="hljs-comment">#     - node_modules/</span><br><br><span class="hljs-attr">build_job:</span><br>  <span class="hljs-attr">stage:</span> <span class="hljs-string">build</span><br>  <span class="hljs-attr">cache:</span><br>    <span class="hljs-attr">key:</span> <span class="hljs-string">&quot;$CI_BUILD_REF_NAME&quot;</span><br>    <span class="hljs-attr">paths:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">node_modules/</span><br>  <span class="hljs-attr">script:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">chcp</span> <span class="hljs-number">65001</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">cmd</span> <span class="hljs-string">/c</span> <span class="hljs-string">version.sh</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">cmd</span> <span class="hljs-string">/c</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">cmd</span> <span class="hljs-string">/c</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">build</span><br>  <span class="hljs-attr">artifacts:</span><br>    <span class="hljs-attr">name:</span> <span class="hljs-string">&quot;%CI_COMMIT_REF_NAME%-%CI_COMMIT_SHORT_SHA%&quot;</span><br>    <span class="hljs-attr">paths:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">dist/</span><br>  <span class="hljs-attr">tags:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-number">9.</span><span class="hljs-string">66_Runner</span><br><br><span class="hljs-comment"># 部署与build使用相同runner使用下面配置，注释deploy2_job</span><br><span class="hljs-comment"># deploy1_job:</span><br><span class="hljs-comment">#   stage: deploy</span><br><span class="hljs-comment">#   variables:</span><br><span class="hljs-comment">#     GIT_STRATEGY: none</span><br><span class="hljs-comment">#   cache: &#123;&#125;</span><br><span class="hljs-comment">#   dependencies: []</span><br><span class="hljs-comment">#   script:</span><br><span class="hljs-comment">#     - chcp 65001</span><br><span class="hljs-comment">#     - xcopy dist C:\test\ /C/Q/E/Y</span><br><span class="hljs-comment">#     拷贝当前目录下所有文件夹及内容</span><br><span class="hljs-comment">#     - xcopy . E:\test /C/Q/E/Y</span><br><span class="hljs-comment">#   only:</span><br><span class="hljs-comment">#     - master</span><br><span class="hljs-comment">#   tags:</span><br><span class="hljs-comment">#     - 9.66_Runner</span><br><br><span class="hljs-comment"># 部署与build使用不同runner使用下面配置，注释deploy1_job</span><br><span class="hljs-attr">deploy2_job:</span><br>  <span class="hljs-attr">stage:</span> <span class="hljs-string">deploy</span><br>  <span class="hljs-attr">variables:</span><br>    <span class="hljs-attr">GIT_STRATEGY:</span> <span class="hljs-string">none</span><br>  <span class="hljs-comment"># cache: &#123;&#125;</span><br>  <span class="hljs-attr">dependencies:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">build_job</span><br>  <span class="hljs-attr">script:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">chcp</span> <span class="hljs-number">65001</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">xcopy</span> <span class="hljs-string">dist</span> <span class="hljs-string">E:\web\autodeploy\</span> <span class="hljs-string">/C/Q/E/Y</span><br>  <span class="hljs-comment">#  only:</span><br>  <span class="hljs-comment">#    - master</span><br>  <span class="hljs-attr">tags:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-number">9.</span><span class="hljs-string">95_Runner</span><br></code></pre></td></tr></table></figure><h2 id="version-sh-文件配置解释"><a href="#version-sh-文件配置解释" class="headerlink" title="version.sh 文件配置解释"></a>version.sh 文件配置解释</h2><blockquote><p>在第三部分中的.gitlab-ci.yml 代码中 build_job 的 script 中有这么一行命令 <code>- cmd /c version.sh</code></p><p>这里通过执行 version.sh 这么一个 bash 命令文件来生成方便开发和测试的一种辅助功能，具体看下图</p></blockquote><img src="/blog/posts/9604e2a7/7version.sh%E6%96%87%E4%BB%B6.png" class="" title="7version.sh文件"><h3 id="version-sh-代码如下"><a href="#version-sh-代码如下" class="headerlink" title="version.sh 代码如下"></a>version.sh 代码如下</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs bash">git rev-list HEAD | sort &gt; config.git-hash<br>LOCALVER=`wc -l config.git-hash | awk <span class="hljs-string">&#x27;&#123;print $1&#125;&#x27;</span>`<br><span class="hljs-keyword">if</span> [ <span class="hljs-variable">$LOCALVER</span> \&gt; 1 ] ; <span class="hljs-keyword">then</span><br>    REVISE=`git rev-list origin/master | sort | join config.git-hash - | wc -l | awk <span class="hljs-string">&#x27;&#123;print $1&#125;&#x27;</span>`<br>VERSION=`git tag`<br><span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;<span class="hljs-variable">$VERSION</span>&quot;</span><br>    <span class="hljs-keyword">if</span> [ <span class="hljs-variable">$REVISE</span> != <span class="hljs-variable">$LOCALVER</span> ] ; <span class="hljs-keyword">then</span><br>        REVISE=<span class="hljs-string">&quot;<span class="hljs-variable">$REVISE</span>+<span class="hljs-subst">$(($LOCALVER-$REVISE)</span>)&quot;</span><br>    <span class="hljs-keyword">fi</span><br>    <span class="hljs-keyword">if</span> git status | grep -q <span class="hljs-string">&quot;modified:&quot;</span> ; <span class="hljs-keyword">then</span><br>        REVISE=<span class="hljs-string">&quot;<span class="hljs-variable">$&#123;REVISE&#125;</span>&quot;</span><br>    <span class="hljs-keyword">fi</span><br><br><span class="hljs-keyword">if</span> [  ! -n <span class="hljs-string">&quot;<span class="hljs-variable">$VERSION</span>&quot;</span> ]; <span class="hljs-keyword">then</span><br>        VERSION=<span class="hljs-string">&quot;v1.0.0&quot;</span><br><span class="hljs-keyword">else</span><br>VERSION=`git describe --tags $(git rev-list --tags --max-count=1)`<br>    <span class="hljs-keyword">fi</span><br><br>GIT_HASH=`git rev-list HEAD -n 1`<br>GIT_SHORT_HASH=`git rev-list HEAD -n 1 | cut -c 1-8`<br>    TIME=`git <span class="hljs-built_in">log</span> --pretty=format:<span class="hljs-string">&quot;%cd&quot;</span> <span class="hljs-variable">$GIT_SHORT_HASH</span> -1`<br><span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;<span class="hljs-variable">$TIME</span>&quot;</span><br><span class="hljs-keyword">else</span><br>    GIT_VERSION=<br>    REVISE=<span class="hljs-string">&quot;x&quot;</span>SHORT_<br><span class="hljs-keyword">fi</span><br>rm -f config.git-hash<br><br>cat version.template | sed <span class="hljs-string">&quot;s/\$FULL_HASH/<span class="hljs-variable">$GIT_HASH</span>/g&quot;</span> | sed <span class="hljs-string">&quot;s/\$FULL_REVISE/<span class="hljs-variable">$REVISE</span>/g&quot;</span> | sed <span class="hljs-string">&quot;s/\$FULL_VERSION/<span class="hljs-variable">$VERSION</span>/g&quot;</span> | sed <span class="hljs-string">&quot;s/\$FULL_TIME/<span class="hljs-variable">$TIME</span>/g&quot;</span> &gt; public/<span class="hljs-variable">$REVISE</span><br>cat version.json.template | sed <span class="hljs-string">&quot;s/\$FULL_HASH/<span class="hljs-variable">$GIT_SHORT_HASH</span>/g&quot;</span> | sed <span class="hljs-string">&quot;s/\$FULL_REVISE/<span class="hljs-variable">$REVISE</span>/g&quot;</span> | sed <span class="hljs-string">&quot;s/\$FULL_VERSION/<span class="hljs-variable">$VERSION</span>/g&quot;</span> | sed <span class="hljs-string">&quot;s/\$FULL_TIME/<span class="hljs-variable">$TIME</span>/g&quot;</span> &gt; public/version.json<br><br><span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;<span class="hljs-variable">$VERSION</span>.<span class="hljs-variable">$REVISE</span>&quot;</span><br><span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;Generated version info : <span class="hljs-variable">$REVISE</span>&quot;</span><br></code></pre></td></tr></table></figure><h3 id="version-template-代码如下："><a href="#version-template-代码如下：" class="headerlink" title="version.template 代码如下："></a>version.template 代码如下：</h3><figure class="highlight stata"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs stata"><span class="hljs-keyword">Version</span> <span class="hljs-variable">$FULL_VERSION</span><br>Revise <span class="hljs-variable">$FULL_REVISE</span><br>Git_hash <span class="hljs-variable">$FULL_HASH</span><br>Git_time <span class="hljs-variable">$FULL_TIME</span><br></code></pre></td></tr></table></figure><h3 id="version-json-template-代码如下："><a href="#version-json-template-代码如下：" class="headerlink" title="version.json.template 代码如下："></a>version.json.template 代码如下：</h3><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs json">&#123;<br>    <span class="hljs-attr">&quot;Tag_version&quot;</span>: <span class="hljs-string">&quot;$FULL_VERSION&quot;</span>,<br>    <span class="hljs-attr">&quot;Commit_count&quot;</span>: <span class="hljs-string">&quot;$FULL_REVISE&quot;</span>,<br>    <span class="hljs-attr">&quot;Current_commit_ID&quot;</span>: <span class="hljs-string">&quot;$FULL_HASH&quot;</span>,<br>    <span class="hljs-attr">&quot;Current_commit_time&quot;</span>: <span class="hljs-string">&quot;$FULL_TIME&quot;</span><br>&#125;<br></code></pre></td></tr></table></figure><h2 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h2><blockquote><p>因 build 环节，可能会出现不同项目有不同的 npm 仓，有的包需要用到私仓或者 npm 公仓；这里做以下补充</p></blockquote><p>第四部分的介绍属于扩展，根据项目，按需加</p><p>下面介绍需要使用多不同的 npm 镜像时如何处理：</p><h3 id="修改-gitlab-ci-yml-文件，如下："><a href="#修改-gitlab-ci-yml-文件，如下：" class="headerlink" title="修改.gitlab-ci.yml 文件，如下："></a>修改.gitlab-ci.yml 文件，如下：</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">stages:</span><br>  <span class="hljs-bullet">-</span> <span class="hljs-string">build</span><br>  <span class="hljs-bullet">-</span> <span class="hljs-string">deploy</span><br><br><span class="hljs-attr">build_job:</span><br>  <span class="hljs-attr">stage:</span> <span class="hljs-string">build</span><br>  <span class="hljs-attr">script:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">chcp</span> <span class="hljs-number">65001</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">cmd</span> <span class="hljs-string">/c</span> <span class="hljs-string">version.sh</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">cmd</span> <span class="hljs-string">/c</span> <span class="hljs-string">build.sh</span><br>  <span class="hljs-attr">artifacts:</span><br>    <span class="hljs-attr">name:</span> <span class="hljs-string">&quot;%CI_COMMIT_REF_NAME%-%CI_COMMIT_SHORT_SHA%&quot;</span><br>    <span class="hljs-attr">paths:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-string">dist/</span><br>  <span class="hljs-attr">tags:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-number">9.</span><span class="hljs-string">101_Runner</span><br><br><span class="hljs-comment"># 部署与build使用相同runner使用下面配置，注释deploy2_job</span><br><span class="hljs-comment"># deploy1_job:</span><br><span class="hljs-comment">#   stage: deploy</span><br><span class="hljs-comment">#   variables:</span><br><span class="hljs-comment">#     GIT_STRATEGY: none</span><br><span class="hljs-comment">#   cache: &#123;&#125;</span><br><span class="hljs-comment">#   dependencies: []</span><br><span class="hljs-comment">#   script:</span><br><span class="hljs-comment">#     - chcp 65001</span><br><span class="hljs-comment">#     - xcopy dist C:\test\ /C/Q/E/Y</span><br><span class="hljs-comment">#   only:</span><br><span class="hljs-comment">#     - master</span><br><span class="hljs-comment">#   tags:</span><br><span class="hljs-comment">#     - 9.66_Runner</span><br><br><span class="hljs-comment"># 部署与build使用不同runner使用下面配置，注释deploy1_job</span><br><span class="hljs-attr">deploy2_job:</span><br>  <span class="hljs-attr">stage:</span> <span class="hljs-string">deploy</span><br>  <span class="hljs-attr">variables:</span><br>    <span class="hljs-attr">GIT_STRATEGY:</span> <span class="hljs-string">none</span><br>  <span class="hljs-comment"># cache: &#123;&#125;</span><br>  <span class="hljs-attr">dependencies:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">build_job</span><br>  <span class="hljs-attr">script:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">chcp</span> <span class="hljs-number">65001</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">xcopy</span> <span class="hljs-string">dist</span> <span class="hljs-string">E:\web\autodeploy\</span> <span class="hljs-string">/C/Q/E/Y</span><br>  <span class="hljs-attr">only:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-string">master</span><br>  <span class="hljs-attr">tags:</span><br>    <span class="hljs-bullet">-</span> <span class="hljs-number">9.</span><span class="hljs-string">95_Runner</span><br></code></pre></td></tr></table></figure><blockquote><p><strong>注意：其中对 build_job 的内容做了修改</strong></p></blockquote><h3 id="与-version-sh-同级，新建-build-sh-文件，添加如下内容"><a href="#与-version-sh-同级，新建-build-sh-文件，添加如下内容" class="headerlink" title="与 version.sh 同级，新建 build.sh 文件，添加如下内容"></a>与 version.sh 同级，新建 build.sh 文件，添加如下内容</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs shell">npm install --registry https://registry.npmjs.org/<br>npm install --registry https://registry.npm.taobao.org/<br>npm install --registry http://registry.npm.rdapp.com/<br>npm run build<br></code></pre></td></tr></table></figure><blockquote><p>这里 install 的第一条和第二条选择一个即可，建议使用 taobao 镜像</p><p><strong>注意：只有在 taobao 镜像也下载不下来库时，就需要值用 npmjs 的镜像</strong></p></blockquote>]]></content>
    
    
    
    <tags>
      
      <tag>Gitlab</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>三维模型资源获取</title>
    <link href="/blog/posts/fdfea9f5.html"/>
    <url>/blog/posts/fdfea9f5.html</url>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>使用 mapbox-gl 加载三维模型，到处寻找也没有找到几个免费重要是能用的。模型分很多种，有 3ds、max、fbx、obj、mtl、blend、c4d、dae、gltf、glb 格式等等，之前一直使用 obj 和 mtl 的，但丢失材质有点严重。</p><h2 id="寻找"><a href="#寻找" class="headerlink" title="寻找"></a>寻找</h2><span id="more"></span><p>机缘巧合之下，下载的包中含有 fbx 格式的，双击竟然能打开，原来是<code>win10</code>自带的 3D 查看器，右上角有一个 3D 资源库，在里面搜了一下，发现好多能用模型，可以另存为 glb 格式。glTF 文件格式有 <code>.gltf</code> 和 <code>.glb</code> 两种。glTF 文件的 buffer 部分在 <code>.gltf</code> 里是用 Base64 编码保存的，有时候也会被抽出来保存为 <code>.bin</code> 文件，导致最后其实有两个文件。</p><h2 id="特别提醒"><a href="#特别提醒" class="headerlink" title="特别提醒"></a>特别提醒</h2><p><div class="note danger">搜索的时候要用英文，不然会查不到！！</div><br><img src="/blog/posts/fdfea9f5/1572943785620.png" class="" width="1572943785620"></p><img src="/blog/posts/fdfea9f5/1572943854268.png" class="" width="1572943854268"><img src="/blog/posts/fdfea9f5/1572944243616.png" class="" width="1572944243616">]]></content>
    
    
    
  </entry>
  
  
  
  <entry>
    <title>使用vscode在leetcode上提交代码</title>
    <link href="/blog/posts/64ddb0a.html"/>
    <url>/blog/posts/64ddb0a.html</url>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在浏览器上使用 leetcode 总觉得不是很方便，如果能和 vscode 结合，应该是如鱼得水、如虎添翼、珠联璧合。</p><img src="/blog/posts/64ddb0a/vscode.jpg" class=""><span id="more"></span><p>事实上是在 vscode 的插件库中已经有了，直接搜索 leetcode，第一个出现的就是。</p><img src="/blog/posts/64ddb0a/1572275879224.png" class="" width="1572275879224"><p>安装使用这个插件是有条件的，要有 node 环境</p><img src="/blog/posts/64ddb0a/1572276033822.png" class="" width="1572276033822"><p>安装好了在左边就有一个 leetcode 的图标， 登入成功后刷新列表，即可查看题目。效果如下：</p><img src="/blog/posts/64ddb0a/1572278115276.png" class="" width="1572278115276"><p>如果没有账号，就要去<a href="http://www.baidu.com/link?url=jhP_IixWVDUkFFNsbhVNBrTOS4Oo_OnhKJeg4oCJm4i">力扣(<em>LeetCode</em>)官网</a>或者<a href="http://www.baidu.com/link?url=E9ThnAR3OQJZeUDbdon7SeBLuvPl0Es9WY69eZVe9cS"><em>LeetCode</em></a>注册账号！！</p><p>初步的可以在<code>// @lc code=end</code>下面结合<code>Code Runner</code>得到初步验证</p><img src="/blog/posts/64ddb0a/1572278329264.png" class="" width="1572278329264"><img src="/blog/posts/64ddb0a/1572278900796.png" class="" width="1572278900796"><p>更多用例的测试需要点<code>Test</code>来验证。</p><p>可以使用<code>Submit</code>来看看自己的代码的排名情况。</p><p>可以去讨论一下或者看看别人的的解决方法。提交到 github，然后就可以到处做题目了！！</p><img src="/blog/posts/64ddb0a/1572278520538.png" class="" width="1572278520538">]]></content>
    
    
    
    <tags>
      
      <tag>Vscode</tag>
      
      <tag>Leetcode</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>WebGL编程指南学习笔记</title>
    <link href="/blog/posts/e6c6b2b1.html"/>
    <url>/blog/posts/e6c6b2b1.html</url>
    
    <content type="html"><![CDATA[<p>个人计算机上使用最广泛的两种三维图形渲染技术是<strong>Direct3D</strong>和<strong>OpenGL</strong>。</p><p>Direct3D 是微软 DirectX 技术的一部分，是一套由微软控制的编程接口 API，主要用在 Windows 平台。<br>OpenGL 由于其开发和免费的特性，在多种平台上都有广泛的使用。</p><span id="more"></span><p><strong>WebGL 是基于 OpenGL ES 2.0 的</strong>。</p><p>OpenGL、OpenGL ES 1.1/2.0/3.0 和 WebGL 的关系。<br><img src="/blog/posts/e6c6b2b1/222059338585675.png" class=""></p><p>从 2.0 版本开始，OpenGL 支持了一项非常重要的特性，即<strong>可编程着色器方法</strong>。该特性被 OpenGL ES 2.0 继承，并成为了 WebGL 1.0 标准的核心部分。</p><p>着色器，使用一种类似于 C 的编程语言实现了精美的视觉效果。编写着色器的语言又称为<strong>着色器语言</strong>。WebGL 基于 OpenGL ES 2.0，使用 GLSL ES 语言编写着色器</p><p>下图显示了 WebGL 程序的结构：<br>　　<img src="/blog/posts/e6c6b2b1/222120593439118.png" class=""></p><p>WebGL 需要两种着色器：</p><h3 id="顶点着色器（-Vertex-shader-）"><a href="#顶点着色器（-Vertex-shader-）" class="headerlink" title="顶点着色器（ Vertex shader ）"></a>顶点着色器（ Vertex shader ）</h3><p>顶点着色器是用来描述顶点特性（如位置、颜色等）的程序。顶点（vertex）是指二维或三维空间中的一个点，如二维或三维图形的端点或交点。</p><h3 id="片元着色器（Fragment-shader）"><a href="#片元着色器（Fragment-shader）" class="headerlink" title="片元着色器（Fragment shader）"></a>片元着色器（Fragment shader）</h3><p>片元着色器是进行逐片元处理过程如光照的程序。片元（fragment）是一个 WebGL 术语，你可以将其理解为像素（图像的单元）。</p>]]></content>
    
    
    
    <tags>
      
      <tag>WebGL</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>Mapbox的表达式</title>
    <link href="/blog/posts/9c1bf78f.html"/>
    <url>/blog/posts/9c1bf78f.html</url>
    
    <content type="html"><![CDATA[<h2 id="Expressions"><a href="#Expressions" class="headerlink" title="Expressions"></a>Expressions</h2><p>mapbox-gl 的表达式可以将任何布局<code>layout</code>属性，绘图<code>paint</code>属性或过滤器<code>filter</code>的值指定为表达式。</p><img src="/blog/posts/9c1bf78f/expression.jpg" class="" title="expression"><span id="more"></span><h3 id="图层样式结构"><a href="#图层样式结构" class="headerlink" title="图层样式结构"></a>图层样式结构</h3><p>图层样式设置的结构为 JSON 结构，根级别结构如下：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs json">&#123;<br>    <span class="hljs-attr">&quot;id&quot;</span>: <span class="hljs-string">&quot;road-layer-1&quot;</span>,<br>    <span class="hljs-attr">&quot;type&quot;</span>: <span class="hljs-string">&quot;line&quot;</span>,<br>    <span class="hljs-attr">&quot;source&quot;</span>: <span class="hljs-string">&quot;RoadSource&quot;</span>,<br>    <span class="hljs-attr">&quot;source-layer&quot;</span>: <span class="hljs-string">&quot;Road&quot;</span>,<br>    <span class="hljs-attr">&quot;layout&quot;</span>: &#123;...&#125;,<br>    <span class="hljs-attr">&quot;paint&quot;</span>: &#123;...&#125;,<br>    <span class="hljs-attr">&quot;minzoom&quot;</span>: <span class="hljs-number">6</span>,<br>    <span class="hljs-attr">&quot;maxzoom&quot;</span>: <span class="hljs-number">17.5</span>,<br>    <span class="hljs-attr">&quot;filter&quot;</span>: [...]<br>&#125;<br></code></pre></td></tr></table></figure><p>字段定义说明如下：</p><p><strong>id</strong>：图层 ID，必填项；说明：（值唯一，不能重复）</p><p><strong>type</strong>：图层渲染类型，必填项；（值域范围，参考本章「概述」）</p><p><strong>source</strong>：所使用的数据源 ID，说明：（当图层类型不为 background 时，该值为必填项）</p><p><strong>source-layer</strong>：所使用的 vector 数据源中的图层标识，说明：（当数据源类型为 vector 时，该值为必填项；其它数据源类型，去除该参数）</p><p><strong>layout</strong>：布局属性</p><p><strong>paint</strong>：绘制属性</p><p><strong>minzoom</strong>：图层可展示的最小缩放等级值，选填项；说明：（值为小数或整数，值域范围[0,24]，不设置该参数则地图允许的最小缩放等级内都可显示）</p><p><strong>maxzoom</strong>：图层可展示的最大缩放等级值，选填项；说明：（值为小数或整数，值域范围[0,24]，不设置该参数则地图允许的最大缩放等级内都可显示）</p><p><strong>filter</strong>：所使用数据源的 features 数据的过滤条件，选填项；说明：（当数据源 features 数据信息和 filter 条件匹配时图层才显示）</p><p>表达式定义了一个公式，用于使用以下描述的<em>运算符</em>计算属性的值。Mapbox GL 提供的表达式运算符集包括：</p><ul><li>Mathematical operators：数学计算器用于数值计算和其他数值相关的属性(如’+’ ‘-‘ ‘*‘ ‘/‘)</li><li>Logical operators：用于操纵布尔值和进行条件决策的逻辑运算符(如’case’ ‘let’)</li><li>String operators：用于操纵字符串的字符串运算符(如 string 转 number)</li><li>Data operators：提供调用数据源要素集属性的接口(如 ‘get’)</li><li>Camera operators：提供定义当前地图视角参数的接口(如 ‘zoom’)</li></ul><p>Expressions 表达式使用类似 Lisp 的语法，表达式数组的第一个元素是一个表示计算器的字符串，例如<code>*</code>或<code>case</code>。后面的元素是表达式的参数，每个参数要么是个原始的值（字符串、数字、布尔值或 null），要么是另一个表达式数组。</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs json">[expression_name, argument_0, argument_1, ...]<br></code></pre></td></tr></table></figure><h3 id="Data-expressions"><a href="#Data-expressions" class="headerlink" title="Data expressions"></a>Data expressions</h3><p>数据表达式是任何能够调用要素数据的表达式，这种表达式使用如下一种数据计算器：<a href="https://www.mapbox.com/mapbox-gl-js/style-spec#expressions-get"><code>get</code></a>、<a href="https://www.mapbox.com/mapbox-gl-js/style-spec#expressions-has"><code>has</code></a>、<a href="https://www.mapbox.com/mapbox-gl-js/style-spec#expressions-id"><code>id</code></a>、<a href="https://www.mapbox.com/mapbox-gl-js/style-spec#expressions-geometry-type"><code>geometry-type</code></a>、<a href="https://www.mapbox.com/mapbox-gl-js/style-spec#expressions-properties"><code>properties</code></a>、or <code>feature-state</code>。数据表达式利用要素集的属性或者状态来决定如何表达要素，它们可以在同一图层中创建不同的数据表达。</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs json">&#123;<br>  <span class="hljs-attr">&quot;circle-color&quot;</span>: [<br>    <span class="hljs-string">&quot;rgb&quot;</span>,<br>    <span class="hljs-comment">// red is higher when feature.properties.temperature is higher</span><br>    [<span class="hljs-string">&quot;get&quot;</span>, <span class="hljs-string">&quot;temperature&quot;</span>],<br>    <span class="hljs-comment">// green is always zero</span><br>    <span class="hljs-number">0</span>,<br>    <span class="hljs-comment">// blue is higher when feature.properties.temperature is lower</span><br>    [<span class="hljs-string">&quot;-&quot;</span>, <span class="hljs-number">100</span>, [<span class="hljs-string">&quot;get&quot;</span>, <span class="hljs-string">&quot;temperature&quot;</span>]]<br>  ]<br>&#125;<br></code></pre></td></tr></table></figure><p>上面这个例子使用<code>get</code>运算符来获取每个要素的温度值，这个结果值被用作 rgb 运算符的属性值，rgb 运算符是用红绿蓝定义颜色的操作符。</p><p>数据表达式可以被用作 filter 的属性值和大多数布局<code>layout</code>属性、绘画<code>paint</code>属性值的计算。然而，一些绘画<code>paint</code>和布局<code>layout</code>属性并不支持数据表达式。支持级别可以在支持列表中查看。<code>feature-state</code>运算符的数据表达式仅适用于绘画<code>paint</code>属性中。</p><h3 id="Camera-expressions"><a href="#Camera-expressions" class="headerlink" title="Camera expressions"></a>Camera expressions</h3><p>一个相机表达式是指任何使用 zoom 操作符的表达式，这些表达式允许一个图层改变地图的缩放等级。相机表达式可以用来创建表现的深度和控制数据密度。</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs json">&#123;<br>  <span class="hljs-attr">&quot;circle-radius&quot;</span>: [<br>    <span class="hljs-string">&quot;interpolate&quot;</span>,<br>    [<span class="hljs-string">&quot;linear&quot;</span>],<br>    [<span class="hljs-string">&quot;zoom&quot;</span>],<br>    <span class="hljs-comment">// zoom is 5 (or less) -&gt; circle radius will be 1px</span><br>    <span class="hljs-number">5</span>,<br>    <span class="hljs-number">1</span>,<br>    <span class="hljs-comment">// zoom is 10 (or greater) -&gt; circle radius will be 5px</span><br>    <span class="hljs-number">10</span>,<br>    <span class="hljs-number">5</span><br>  ]<br>&#125;<br></code></pre></td></tr></table></figure><p>这个例子使用<code>interpolate</code>插值运算符用来定义缩放等级和圆大小的线性相对关系，在这个例子中，表达式表明了圆半径在 zoom 等级为 5 或者更低时为 1 像素，在 zoom 等级为 10 或者更高时为 5 像素。在此之间的 zoom，半径线性的在 1 到 5 像素之间变化。</p><p>相机表达式在任何表达式使用的地方都能使用，然后当相机表达式用于布局<code>layout</code>属性或者绘画<code>paint</code>属性的值时，它必须是以下面形式中的一种：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs json">[ <span class="hljs-string">&quot;interpolate&quot;</span>, interpolation, [<span class="hljs-string">&quot;zoom&quot;</span>], ... ]<br></code></pre></td></tr></table></figure><p>或者：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs json">[ <span class="hljs-string">&quot;step&quot;</span>, [<span class="hljs-string">&quot;zoom&quot;</span>], ... ]<br></code></pre></td></tr></table></figure><p>或者：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs json">[<br>    <span class="hljs-string">&quot;let&quot;</span>,<br>    ... variable bindings...,<br>    [ <span class="hljs-string">&quot;interpolate&quot;</span>, interpolation, [<span class="hljs-string">&quot;zoom&quot;</span>], ... ]<br>]<br></code></pre></td></tr></table></figure><p>或者：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs json">[<br>    <span class="hljs-string">&quot;let&quot;</span>,<br>    ... variable bindings...,<br>    [ <span class="hljs-string">&quot;step&quot;</span>, [<span class="hljs-string">&quot;zoom&quot;</span>], ... ]<br>]<br></code></pre></td></tr></table></figure><p>也就是说，用于布局<code>layout</code>属性和绘画<code>paint</code>属性时，<code>zoom</code>操作符只能被用作为<code>interpolate</code>、<code>step</code>或者<code>let</code>这三种操作符的内在操作符。在布局<code>layout</code>属性和绘画<code>paint</code>属性中使用相机表达式有个重要的区别，绘画<code>paint</code>属性相机表达式当 zoom 等级发生即使很小变化时就能够重绘，例如一个绘画<code>paint</code>属性相机表达式会持续的变化，当 zoom 等级在 4.1 和 4.6 之间变动时。与此同时，布局<code>layout</code>属性相机表达式只会在 zoom 整数跳变时计算值，例如在 4.1 到 4.6 之间变化时不会重新计算值，除非是重 5 到 4.</p><h3 id="Composition"><a href="#Composition" class="headerlink" title="Composition"></a>Composition</h3><p>一个单独的表达式可能使用 data 操作符、相机操作符和其他操作符的混合。这种组合表达式使一个图层的渲染决定于缩放等级和单独要素属性的组合。</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs json">&#123;<br>  <span class="hljs-attr">&quot;circle-radius&quot;</span>: [<br>    <span class="hljs-string">&quot;interpolate&quot;</span>,<br>    [<span class="hljs-string">&quot;linear&quot;</span>],<br>    [<span class="hljs-string">&quot;zoom&quot;</span>],<br>    <span class="hljs-comment">// when zoom is 0, set each feature&#x27;s circle radius to the value of its &quot;rating&quot; property</span><br>    <span class="hljs-number">0</span>,<br>    [<span class="hljs-string">&quot;get&quot;</span>, <span class="hljs-string">&quot;rating&quot;</span>],<br>    <span class="hljs-comment">// when zoom is 10, set each feature&#x27;s circle radius to four times the value of its &quot;rating&quot; property</span><br>    <span class="hljs-number">10</span>,<br>    [<span class="hljs-string">&quot;*&quot;</span>, <span class="hljs-number">4</span>, [<span class="hljs-string">&quot;get&quot;</span>, <span class="hljs-string">&quot;rating&quot;</span>]]<br>  ]<br>&#125;<br></code></pre></td></tr></table></figure><p>一个同时使用了 data 和 camera 运算符的表达式同时考虑了 data 和 camera 表达式。</p><p><a href="http://dev.minedata.cn/api/dev/js/guide/layer/style">http://dev.minedata.cn/api/dev/js/guide/layer/style</a></p><p>filter 规格</p><p>fliter 表示从所有图层过滤出特定特征的图层，有以下几种过滤形式：</p><p>1、存在过滤：</p><p>表达式形式：[way, key]</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs csharp">存在过滤主要有“has”、“!has”两种形式<br><br>[<span class="hljs-meta"><span class="hljs-meta-string">&quot;has&quot;</span>, <span class="hljs-meta-string">&quot;count&quot;</span></span>]，表示过滤出存在属性<span class="hljs-string">&quot;count&quot;</span>的所有的feature数据<br>[<span class="hljs-meta"><span class="hljs-meta-string">&quot;!has&quot;</span>,<span class="hljs-meta-string">&quot;count&quot;</span></span>]，表示过滤出不存在属性<span class="hljs-string">&quot;count&quot;</span>的所有的feature数据<br></code></pre></td></tr></table></figure><p>2、比较过滤：</p><p>表达式形式：[way, key, value]</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs awk">比较过滤有等于“==”、大于“&gt;”、小于“&lt;”、不等于“!=”、大于等于“&gt;=”、小于等于“&lt;=”几种形式<br><br>[<span class="hljs-string">&quot;==&quot;</span>, <span class="hljs-string">&quot;count&quot;</span>, <span class="hljs-string">&quot;1000&quot;</span>]，表示过滤出属性<span class="hljs-string">&quot;count&quot;</span>值为<span class="hljs-number">1000</span>的feature数据，<br><span class="hljs-regexp">//</span>  注意此时数据源name存储的值为<span class="hljs-string">&quot;1000&quot;</span>而不是<span class="hljs-number">1000</span> 过滤时数据类型是严格匹配的<br></code></pre></td></tr></table></figure><p>3、成员过滤：</p><p>计算形式：[way, key, v0,v1，…，vn]</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs awk">成员过滤主要有<span class="hljs-string">&quot;in&quot;</span>、<span class="hljs-string">&quot;!in&quot;</span>两种形式<br><br>[<span class="hljs-string">&quot;in&quot;</span>, <span class="hljs-string">&quot;name&quot;</span>, <span class="hljs-string">&quot;point&quot;</span>, <span class="hljs-string">&quot;fill&quot;</span>, <span class="hljs-string">&quot;line&quot;</span>]，<br><span class="hljs-regexp">//</span>  表示过滤出属性<span class="hljs-string">&quot;name&quot;</span>值为<span class="hljs-string">&quot;point&quot;</span>, <span class="hljs-string">&quot;fill&quot;</span>, <span class="hljs-string">&quot;line&quot;</span>其中任一一个的feature数据<br></code></pre></td></tr></table></figure><p>4、组过滤：</p><p>计算形式：[way, key, v0,v1，…，vn]</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs awk">组过滤主要有组包含<span class="hljs-string">&quot;arrin&quot;</span>、组不包含<span class="hljs-string">&quot;!arrin&quot;</span>两种形式<br><br>[<span class="hljs-string">&quot;arrin&quot;</span>, <span class="hljs-string">&quot;name&quot;</span>, <span class="hljs-string">&quot;point&quot;</span>, <span class="hljs-string">&quot;fill&quot;</span>]，<br><span class="hljs-regexp">//</span>  表示过滤出属性<span class="hljs-string">&quot;name&quot;</span>值为[<span class="hljs-string">&quot;point&quot;</span>, <span class="hljs-string">&quot;fill&quot;</span>]、[<span class="hljs-string">&quot;point&quot;</span>]、[<span class="hljs-string">&quot;fill&quot;</span>]其中任一一个的feature数据<br><span class="hljs-regexp">//</span>  属性<span class="hljs-string">&quot;name&quot;</span>值为数组形式<br></code></pre></td></tr></table></figure><p>5、模糊过滤：</p><p>计算形式：[way, key, value]</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">模糊过滤有“<span class="hljs-keyword">like</span>”、开始于“<span class="hljs-keyword">start</span>”、结束于“<span class="hljs-keyword">end</span>”几种形式<br><br>[&quot;like&quot;, &quot;code&quot;, &quot;101&quot;]，表示过滤出属性&quot;code&quot;值包含<span class="hljs-number">101</span>的feature数据<br>//  属性&quot;code&quot;值为字符形式<br></code></pre></td></tr></table></figure><p>6、组合过滤：</p><p>计算形式：[way, f0,f1，…，fn]</p><figure class="highlight prolog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs prolog">组合过滤主要有等于<span class="hljs-string">&quot;all&quot;</span>、<span class="hljs-string">&quot;any&quot;</span>、<span class="hljs-string">&quot;none&quot;</span>三种形式<br><span class="hljs-string">&quot;all&quot;</span>表示满足所有过滤条件的数据，<span class="hljs-string">&quot;any&quot;</span>表示满足任一一个过滤条件的数据，<span class="hljs-string">&quot;none&quot;</span>表示过滤出不满足所有过滤条件的数据<br><br>[<span class="hljs-string">&quot;all&quot;</span>, [<span class="hljs-string">&quot;&lt;=&quot;</span>, <span class="hljs-string">&quot;count&quot;</span>, <span class="hljs-number">34</span>], [<span class="hljs-string">&quot;like&quot;</span>, <span class="hljs-string">&quot;code&quot;</span>, <span class="hljs-string">&quot;101&quot;</span>]]，<br>//  表示过滤出属性<span class="hljs-string">&quot;count&quot;</span>满足大于等于<span class="hljs-number">34</span>，属性<span class="hljs-string">&quot;code&quot;</span>值包含<span class="hljs-number">101</span>的feature数据<br></code></pre></td></tr></table></figure><p>函数对象语句</p><p>图层样式的某些 layout 或 paint 属性值支持函数对象语句的方式，属性值的最终结果由当前缩放等级或 feature 属性值进行相关计算而取得。</p><p>函数对象语句的结构为 JSON 结构，根级别结构如下：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs json">&#123;<br>    <span class="hljs-attr">&quot;property&quot;</span>: <span class="hljs-string">&quot;kind&quot;</span>,<br>    <span class="hljs-attr">&quot;base&quot;</span>: <span class="hljs-number">1</span>,<br>    <span class="hljs-attr">&quot;type&quot;</span>: <span class="hljs-string">&quot;interval&quot;</span>,<br>    <span class="hljs-attr">&quot;default&quot;</span>: <span class="hljs-string">&quot;#000000&quot;</span>,<br>    <span class="hljs-attr">&quot;stops&quot;</span>: [...]<br>&#125;<br></code></pre></td></tr></table></figure><p>字段定义说明如下：</p><p><strong>property</strong>：具体的 feature 属性标识；（非必输项）</p><p><strong>base</strong>：差值运算的曲率指数基数，控制最终计算结果值的增长率，值越大，最终计算结果值越大，值为 1 时，函数采用线性计算方式；（数值类型，默认值为 1）</p><p><strong>stops</strong>：差值运算项，定义输入值和输出值的集合，每一个 stop 由一个输入值和一个输出值组成；（数组形式，当 type 不为’identity’时，stops 为必输项）</p><p><strong>type</strong>：函数对象计算类型；（值域为[“identity”, “exponential”,”interval”, “categorical”]，默认值为’interval’）</p><p><strong>1、identity</strong>：恒等类型，最终输出值完全等于输入值；</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-comment">/*示例：建筑物高度取用建筑物feature中的的属性字段levels的值*/</span><br>&#123;<br>    <span class="hljs-attr">&quot;extrusion-height&quot;</span>: &#123;<br>        <span class="hljs-attr">&quot;type&quot;</span>: <span class="hljs-string">&quot;identity&quot;</span>,<br>        <span class="hljs-attr">&quot;property&quot;</span>: <span class="hljs-string">&quot;levels&quot;</span><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p><strong>2、exponential</strong>：指数类型，最终输出值由 stops 中的差值项进行区间范围内的指数级差值计算生成，stops 中的输入参数必须为数值类型；</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-comment">/*示例：线的颜色值由feature中的price值进行区间指数级差值运算取得*/</span><br>&#123;<br>    <span class="hljs-attr">&quot;line-width&quot;</span>: &#123;<br>        <span class="hljs-attr">&quot;property&quot;</span>: <span class="hljs-string">&quot;price&quot;</span>,<br>        <span class="hljs-attr">&quot;type&quot;</span>: <span class="hljs-string">&quot;exponential&quot;</span>,<br>        <span class="hljs-attr">&quot;stops&quot;</span>: [[<span class="hljs-number">0</span>, <span class="hljs-number">1</span>],[<span class="hljs-number">10</span>, <span class="hljs-number">2</span>],[<span class="hljs-number">200</span>, <span class="hljs-number">3</span>],[<span class="hljs-number">300</span>, <span class="hljs-number">4</span>]],<br>        <span class="hljs-attr">&quot;default&quot;</span>: <span class="hljs-number">1</span><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p><strong>3、interval</strong>：区间类型，最终输出值由 stops 中的差值项进行区间范围内的阶梯型差值计算生成，stops 中的输入参数必须为数值类型；</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-comment">/*示例：线的颜色值由feature中的status值进行区间差值运算取得*/</span><br>&#123;<br>    <span class="hljs-attr">&quot;line-color&quot;</span>: &#123;<br>        <span class="hljs-attr">&quot;property&quot;</span>: <span class="hljs-string">&quot;status&quot;</span>,<br>        <span class="hljs-attr">&quot;type&quot;</span>: <span class="hljs-string">&quot;interval&quot;</span>,<br>        <span class="hljs-attr">&quot;stops&quot;</span>: [[<span class="hljs-number">0</span>, <span class="hljs-string">&quot;#999999&quot;</span>],[<span class="hljs-number">1</span>, <span class="hljs-string">&quot;#66cc00&quot;</span>],[<span class="hljs-number">2</span>, <span class="hljs-string">&quot;#ff9900&quot;</span>],[<span class="hljs-number">3</span>, <span class="hljs-string">&quot;#cc0000&quot;</span>],[<span class="hljs-number">4</span>, <span class="hljs-string">&quot;#9d0404&quot;</span>]],<br>        <span class="hljs-attr">&quot;default&quot;</span>: <span class="hljs-string">&quot;#66cc00&quot;</span><br>    &#125;<br>&#125;<br><br><span class="hljs-comment">/*示例：表示线宽根据zoom的值进行差值运算，当不添加type属性时，默认为interval类型*/</span><br>&#123;<br>    <span class="hljs-attr">&quot;line-width&quot;</span>: &#123;<br>        <span class="hljs-attr">&quot;base&quot;</span>: <span class="hljs-number">1.2</span>,<br>        <span class="hljs-attr">&quot;stops&quot;</span>: [[<span class="hljs-number">5</span>, <span class="hljs-number">0.8</span>], [<span class="hljs-number">20</span>, <span class="hljs-number">6</span>]]<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p><strong>4、categorical</strong>：种别类型，最终输出值完全匹配 stops 中的输入值对应的输出值；</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-comment">/*示例：面颜色由feature中的的属性字段space_type的值匹配stop中的输入值取得输出值*/</span><br>&#123;<br>    <span class="hljs-attr">&quot;line-color&quot;</span>: &#123;<br>        <span class="hljs-attr">&quot;property&quot;</span>: <span class="hljs-string">&quot;space_type&quot;</span>,<br>        <span class="hljs-attr">&quot;type&quot;</span>: <span class="hljs-string">&quot;categorical&quot;</span>,<br>        <span class="hljs-attr">&quot;stops&quot;</span>: [[<span class="hljs-number">1</span>, <span class="hljs-string">&quot;#f8e4d4&quot;</span>], [<span class="hljs-number">3</span>, <span class="hljs-string">&quot;#f5e8ca&quot;</span>], [<span class="hljs-number">5</span>, <span class="hljs-string">&quot;#f1d4ef&quot;</span>], [<span class="hljs-number">7</span>, <span class="hljs-string">&quot;#f7e8c3&quot;</span>], [<span class="hljs-number">9</span>, <span class="hljs-string">&quot;#f0d3ef&quot;</span>]]<br>        <span class="hljs-string">&quot;default&quot;</span>: <span class="hljs-string">&quot;#f1ebe7&quot;</span><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p><strong>default</strong>：默认值，当差值运算没有结果时取用默认值；会在以下境况中遇到：</p><p>1、函数对象为<strong>categorical</strong>类型：当 feature 属性值不匹配 stops 中的输入值时；</p><p>2、函数对象为<strong>identity</strong>类型：当 feature 属性值不存在或属性值无效时；</p><p>3、函数对象为<strong>interval 或 exponential</strong>类型：当 feature 属性值不是数值类型时；</p>]]></content>
    
    
    
    <tags>
      
      <tag>Mapboxgl</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>Mapbox加载空白地图</title>
    <link href="/blog/posts/e1a3a842.html"/>
    <url>/blog/posts/e1a3a842.html</url>
    
    <content type="html"><![CDATA[<p>由于 mapbox 的服务器在国外，在开发的时候有可能加载很慢，而且大多时候与背景地图无关，此时可以加载一个空白的地图来增加加载速度从而提高开发效率。</p><img src="/blog/posts/e1a3a842/1571374714735.jpg" class="" width="1571374714735"><span id="more"></span><p>代码如下：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><code class="hljs javascript">&lt;!DOCTYPE html&gt;<br><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span></span><br><span class="xml">  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span></span><br><span class="xml">    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">&quot;utf-8&quot;</span> /&gt;</span></span><br><span class="xml">    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Add an image<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span></span><br><span class="xml">    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span></span></span><br><span class="hljs-tag"><span class="xml">      <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;viewport&quot;</span></span></span><br><span class="hljs-tag"><span class="xml">      <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;initial-scale=1,maximum-scale=1,user-scalable=no&quot;</span></span></span><br><span class="hljs-tag"><span class="xml">    /&gt;</span></span><br><span class="xml">    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.1/mapbox-gl.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span><br><span class="xml">    <span class="hljs-tag">&lt;<span class="hljs-name">link</span></span></span><br><span class="hljs-tag"><span class="xml">      <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.1/mapbox-gl.css&quot;</span></span></span><br><span class="hljs-tag"><span class="xml">      <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span></span></span><br><span class="hljs-tag"><span class="xml">    /&gt;</span></span><br><span class="xml">    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css"></span></span><br><span class="css"><span class="xml">      <span class="hljs-selector-tag">body</span> &#123;</span></span><br><span class="css"><span class="xml">        <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;</span></span><br><span class="css"><span class="xml">        <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;</span></span><br><span class="css"><span class="xml">      &#125;</span></span><br><span class="css"><span class="xml">      <span class="hljs-selector-id">#map</span> &#123;</span></span><br><span class="css"><span class="xml">        <span class="hljs-attribute">position</span>: absolute;</span></span><br><span class="css"><span class="xml">        <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;</span></span><br><span class="css"><span class="xml">        <span class="hljs-attribute">bottom</span>: <span class="hljs-number">0</span>;</span></span><br><span class="css"><span class="xml">        <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;</span></span><br><span class="css"><span class="xml">      &#125;</span></span><br><span class="css"><span class="xml">    </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span></span><br><span class="xml">  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span></span><br><span class="xml">  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span></span><br><span class="xml">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;map&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span><br><span class="xml">    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript"></span></span><br><span class="javascript"><span class="xml">      <span class="hljs-keyword">const</span> blankStyle = &#123;</span></span><br><span class="javascript"><span class="xml">        <span class="hljs-attr">version</span>: <span class="hljs-number">8</span>,</span></span><br><span class="javascript"><span class="xml">        <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;BlankMap&quot;</span>,</span></span><br><span class="javascript"><span class="xml">        <span class="hljs-attr">sources</span>: &#123;&#125;,</span></span><br><span class="javascript"><span class="xml">        <span class="hljs-attr">layers</span>: [</span></span><br><span class="javascript"><span class="xml">&#123;</span></span><br><span class="javascript"><span class="xml">        <span class="hljs-attr">id</span>: <span class="hljs-string">&#x27;background&#x27;</span>,</span></span><br><span class="javascript"><span class="xml">        <span class="hljs-attr">type</span>: <span class="hljs-string">&#x27;background&#x27;</span>,</span></span><br><span class="javascript"><span class="xml">        <span class="hljs-attr">paint</span>: &#123; <span class="hljs-string">&#x27;background-color&#x27;</span>: <span class="hljs-string">&#x27;#08294A&#x27;</span> &#125; <span class="hljs-comment">/* 背景颜色 */</span></span></span><br><span class="javascript"><span class="xml">      &#125;</span></span><br><span class="javascript"><span class="xml">]</span></span><br><span class="javascript"><span class="xml">      &#125;;</span></span><br><span class="javascript"><span class="xml"></span></span><br><span class="javascript"><span class="xml">      <span class="hljs-keyword">var</span> map = <span class="hljs-keyword">new</span> mapboxgl.Map(&#123;</span></span><br><span class="javascript"><span class="xml">        <span class="hljs-attr">container</span>: <span class="hljs-string">&quot;map&quot;</span>,</span></span><br><span class="javascript"><span class="xml">        <span class="hljs-attr">zoom</span>: <span class="hljs-number">3</span>,</span></span><br><span class="javascript"><span class="xml">        <span class="hljs-attr">center</span>: [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>],</span></span><br><span class="javascript"><span class="xml">        <span class="hljs-attr">style</span>: blankStyle</span></span><br><span class="javascript"><span class="xml">      &#125;);</span></span><br><span class="javascript"><span class="xml">    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span><br><span class="xml">  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span></span><br><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span></span><br></code></pre></td></tr></table></figure>]]></content>
    
    
    
    <tags>
      
      <tag>Mapboxgl</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>Threejs学习和总结进阶篇</title>
    <link href="/blog/posts/ab44f639.html"/>
    <url>/blog/posts/ab44f639.html</url>
    
    <content type="html"><![CDATA[<h2 id="相机控制器Controls"><a href="#相机控制器Controls" class="headerlink" title="相机控制器Controls"></a>相机控制器Controls</h2><p>相机的基本内容我们已经了解。</p><img src="/blog/posts/ab44f639/sample-scene-threejs.jpg" class=""><span id="more"></span><p>正常的项目中，大家的需求都是不一样的，又通常会碰上需求中途改变的情况，我们之前做的简易版相机控制器很难满足此类项目对相机的操作需求。而且，造轮子的前提是当前的框架以及插件已经无法满足自身的需求时，才会考虑造轮子。要不然，项目的进度会被拖得很慢，甚至有可能因此而错过红利期。</p><p>好在 Three.js 官方和同道中的朋友们给我们提供了很多相关的插件，我们可以根据需求引入相关的插件来实现需求，本文我们就来看一下官方案例中提供的相机控制器。</p><p>从官网下载的代码包里可以发现有很多的相机控制器，文件夹地址为：<code>/examples/js/controls/</code>，里面的文件插件都是和控制相机和控制模型相关的插件，我们罗列一下相关插件：</p><ul><li>DeviceOrientationControls：陀螺仪相机控制器，实现移动端陀螺仪控制相机。</li><li>DragControls：控制鼠标拖拽移动物体的功能。</li><li>EditorControls：实现相机的旋转、缩放、平移功能，相对于 OrbitControls 的功能差不少，不建议使用。</li><li>FirstPersonControls：第一视角相机控制器。</li><li>FlyControls：飞行相机控制器。</li><li>OrbitControls：轨道控制器。</li><li>OrthographicTrackballControls：正交轨迹球控制器——正交相机使用的轨迹球控制器。</li><li>TrackballControls：轨迹球控制器——透视相机使用的轨迹球控制器。</li><li>PointerLockControls：鼠标锁定相机控制器。</li><li>TransformControls：控制模型位置、缩放、旋转的控制器。</li><li>VRControls：实现 VR 双屏相机控制器。</li></ul><p>由于篇幅有限，上面的控制器无法一一介绍，我们将重点介绍三种常用的相机控制器。</p><h3 id="OrbitControls"><a href="#OrbitControls" class="headerlink" title="OrbitControls"></a>OrbitControls</h3><p>OrbitControls 控制器是我们常用的相机控制器，它的功能丰富，使用简单，为大多数项目的使用插件。</p><h4 id="使用操作"><a href="#使用操作" class="headerlink" title="使用操作"></a>使用操作</h4><p>使用 OrbitControls 控制器我们可以实现旋转、缩放、平移等功能，下面简单列一下 OrbitControls 控制器的操作方法：</p><ul><li>围绕焦点旋转：使用鼠标左键拖拽；</li><li>放大和缩小：使用鼠标中键按住拖拽或者鼠标中键滑动滚轮；</li><li>平移相机：按住鼠标右键拖拽或者使用键盘的上下左右键。</li></ul><h4 id="控制器引入"><a href="#控制器引入" class="headerlink" title="控制器引入"></a>控制器引入</h4><p>在项目中使用 OrbitControls 控制器，可分为下面几步。</p><ul><li>首先，将插件文件引入到项目中：</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;../js/OrbitControls.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br></code></pre></td></tr></table></figure><ul><li>然后，通过相机和渲染器的 Dom 对象实例化相机：</li></ul><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">control = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">OrbitControls(<span class="hljs-params">camera</span>, <span class="hljs-params">renderer</span>.<span class="hljs-params">domElement</span>)</span>;<br></code></pre></td></tr></table></figure><ul><li>最后，在每一帧渲染里面更新相机的位置：</li></ul><figure class="highlight mel"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs mel">function <span class="hljs-keyword">render</span>() &#123;<br>    <span class="hljs-keyword">control</span>.update();<br>    <span class="hljs-keyword">renderer</span>.<span class="hljs-keyword">render</span>(scene, <span class="hljs-keyword">camera</span>);<br>&#125;<br></code></pre></td></tr></table></figure><p>这样，我们就完成了一个最简单的 OrbitControls 控制器使用。</p><h4 id="属性和方法"><a href="#属性和方法" class="headerlink" title="属性和方法"></a>属性和方法</h4><p>OrbitControls 控制器最大的优势就是有丰富的配置项，供我们修改来实现项目中的需求，接下来我们看看有哪些属性配置。</p><div class="table-container"><table><thead><tr><th style="text-align:left">属性</th><th style="text-align:left">描述</th></tr></thead><tbody><tr><td style="text-align:left">enabled</td><td style="text-align:left">是否开启当前控制器，默认值是 True，如果设置为 False，将无法通过操作修改相机。</td></tr><tr><td style="text-align:left">target</td><td style="text-align:left">控制器的焦点位置，是一个 <code>THREE.Vector3</code> 对象，默认是 <code>(0, 0, 0)</code></td></tr><tr><td style="text-align:left">minDistance</td><td style="text-align:left">相机距离焦点的最近距离，默认值是0。 此属性适用于透视相机 PerspectiveCamera。</td></tr><tr><td style="text-align:left">maxDistance</td><td style="text-align:left">相机距离焦点的最远距离，默认值是 Infinity（无限远）， 此属性适用于透视相机 PerspectiveCamera。</td></tr><tr><td style="text-align:left">minZoom</td><td style="text-align:left">相机距离焦点的最近距离，默认值是0，此属性适用于正交相机 OrthographicCamera。</td></tr><tr><td style="text-align:left">maxZoom</td><td style="text-align:left">相机距离焦点的最远距离，默认值是 Infinity（无限远），此属性适用于正交相机 OrthographicCamera。</td></tr><tr><td style="text-align:left">minPolarAngle</td><td style="text-align:left">相机位置和焦点与焦点和最上方组成的最小夹角限制，默认值是0。</td></tr><tr><td style="text-align:left">maxPolarAngle</td><td style="text-align:left">相机位置和焦点与焦点和最上方组成的最大夹角限制，默认值是 Math.PI，也就是180度角。</td></tr><tr><td style="text-align:left">minAzimuthAngle</td><td style="text-align:left">当前相机沿水平方向顺时针旋转的弧度，默认值是 <code>- Infinity</code>。</td></tr><tr><td style="text-align:left">maxAzimuthAngle</td><td style="text-align:left">当前相机沿水平方向逆时针旋转的弧度，默认值是 <code>Infinity</code>。</td></tr><tr><td style="text-align:left">enableDamping</td><td style="text-align:left">是否开启拖拽惯性移动，即拖拽停止相机会有缓慢停止的距离移动，默认值是 False。</td></tr><tr><td style="text-align:left">dampingFactor</td><td style="text-align:left">拖拽惯性移动的阻力，默认值是 0.25。</td></tr><tr><td style="text-align:left">enableZoom</td><td style="text-align:left">是否开启缩放操作，默认值是 True。</td></tr><tr><td style="text-align:left">zoomSpeed</td><td style="text-align:left">缩放速度，默认值是 1.0。</td></tr><tr><td style="text-align:left">enableRotate</td><td style="text-align:left">是否开启相机绕焦点旋转操作，默认值是 True。</td></tr><tr><td style="text-align:left">rotateSpeed</td><td style="text-align:left">旋转速度，默认值是 1.0。</td></tr><tr><td style="text-align:left">enablePan</td><td style="text-align:left">是否开启相机平移操作，默认值是 True。</td></tr><tr><td style="text-align:left">panSpeed</td><td style="text-align:left">平移的速度，默认值是 1.0。</td></tr><tr><td style="text-align:left">screenSpacePanning</td><td style="text-align:left">修改相机平移的方向，默认值是 False，即沿 x 轴正负方向和 y 轴正负方向移动。可选值是 True，可以修改为沿 x 轴正负方向和 y 轴正负方向移动。</td></tr><tr><td style="text-align:left">keyPanSpeed</td><td style="text-align:left">键盘上下左右键移动相机的速度，默认值是 7.0。</td></tr><tr><td style="text-align:left">autoRotate</td><td style="text-align:left">当前相机是否自动旋转，默认值是 False，不自动旋转。</td></tr><tr><td style="text-align:left">autoRotateSpeed</td><td style="text-align:left">自动旋转的速度，默认值是 2.0，即渲染满60帧的情况下30秒旋转360度。</td></tr><tr><td style="text-align:left">enableKeys</td><td style="text-align:left">是否开启键盘控制先机平移，默认值是 True。</td></tr></tbody></table></div><p>OrbitControls 控制器的属性配置介绍完了，我们看看 OrbitControls 控制器还有那些方法。</p><h5 id="update"><a href="#update" class="headerlink" title="update()"></a><strong>update()</strong></h5><p>OrbitControls 控制器更新相机的方法，需要在每一帧里面调用。</p><h5 id="reset"><a href="#reset" class="headerlink" title="reset()"></a><strong>reset()</strong></h5><p>重置方法，相机回到初始位置。</p><h5 id="dispose"><a href="#dispose" class="headerlink" title="dispose()"></a><strong>dispose()</strong></h5><p>销毁当前实例化的 OrbitControls 控制器。</p><h5 id="change-回调"><a href="#change-回调" class="headerlink" title="change 回调"></a><strong>change 回调</strong></h5><p>我们还可以监听相机改变回调，如果控制器修改了相机，将会产生一个回调：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs javascript">controls.addEventListener(<span class="hljs-string">&#x27;change&#x27;</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>&#123;<br>    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;相机动了！&quot;</span>);<br>&#125;);<br></code></pre></td></tr></table></figure><p>最后，我附上 OrbitControls 控制器案例：</p><ul><li><a href="https://johnson2heng.github.io/GitChat-Three.js/09第九节 controls/OrbitControls.html">点击这里</a></li></ul><p>也可以从这里获取案例源码：</p><ul><li><a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/09第九节 controls/OrbitControls.html">点击这里</a></li></ul><h3 id="TrackballControls"><a href="#TrackballControls" class="headerlink" title="TrackballControls"></a>TrackballControls</h3><p>TrackballControls 控制器比 OrbitControls 控制器更自由，TrackballControls 控制器能够沿焦点进行球形旋转，没有死角，但比 OrbitControls 控制器少一些相关的功能配置。如何选择使用它们还是看项目需求，接下来还是先看如何操作。</p><p>注意，透视相机和正交相机使用的不是一个插件，此插件为透视相机使用，如果是正交相机请使用 OrthographicTrackballControls。</p><h4 id="使用操作-1"><a href="#使用操作-1" class="headerlink" title="使用操作"></a>使用操作</h4><p>使用 TrackballControls 控制器我们可以实现旋转、缩放、平移等功能，下面说一下如何使用 TrackballControls 控制器进行操作：</p><ul><li>围绕焦点旋转：使用鼠标左键拖拽；</li><li>放大和缩小：使用鼠标中键按住拖拽或者鼠标中键滑动滚轮；</li><li>平移相机：按住鼠标右键拖拽或者使用键盘的上下左右键。</li></ul><p>TrackballControls 控制器和 OrbitControls 控制器的操作相同，没什么可说的。</p><h4 id="控制器引入-1"><a href="#控制器引入-1" class="headerlink" title="控制器引入"></a>控制器引入</h4><p>在项目中使用 TrackballControls 控制器和 OrbitControls 控制器的方法雷同，分为下面几步。</p><ul><li>首先，将插件文件引入到项目中：</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;../js/TrackballControls.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br></code></pre></td></tr></table></figure><ul><li>然后，通过相机和渲染器的 Dom 对象实例化相机：</li></ul><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">control = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">TrackballControls(<span class="hljs-params">camera</span>, <span class="hljs-params">renderer</span>.<span class="hljs-params">domElement</span>)</span>;<br></code></pre></td></tr></table></figure><ul><li>最后，在每一帧渲染里面更新相机的位置：</li></ul><figure class="highlight mel"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs mel">function <span class="hljs-keyword">render</span>() &#123;<br>    <span class="hljs-keyword">control</span>.update();<br>    <span class="hljs-keyword">renderer</span>.<span class="hljs-keyword">render</span>(scene, <span class="hljs-keyword">camera</span>);<br>&#125;<br></code></pre></td></tr></table></figure><h4 id="属性和方法-1"><a href="#属性和方法-1" class="headerlink" title="属性和方法"></a>属性和方法</h4><div class="table-container"><table><thead><tr><th style="text-align:left">属性</th><th style="text-align:left">描述</th></tr></thead><tbody><tr><td style="text-align:left">enabled</td><td style="text-align:left">是否开启当前控制器，默认值是 True，如果设置为 False，将无法通过操作修改相机。</td></tr><tr><td style="text-align:left">rotateSpeed</td><td style="text-align:left">控制相机旋转速度，默认值是 3.0。</td></tr><tr><td style="text-align:left">zoomSpeed</td><td style="text-align:left">控制相机缩放速度，默认值是 1.2。</td></tr><tr><td style="text-align:left">panSpeed</td><td style="text-align:left">控制相机平移速度，默认值是 0.3。</td></tr><tr><td style="text-align:left">noRotate</td><td style="text-align:left">关闭相机旋转，默认 False，开启。</td></tr><tr><td style="text-align:left">noZoom</td><td style="text-align:left">关闭相机缩放，默认 False，开启。</td></tr><tr><td style="text-align:left">noPan</td><td style="text-align:left">关闭相机移动，默认 False 开启。</td></tr><tr><td style="text-align:left">staticMoving</td><td style="text-align:left">关闭拖拽惯性移动 默认值 False，开启。</td></tr><tr><td style="text-align:left">dynamicDampingFactor</td><td style="text-align:left">拖拽惯性移动阻力，默认值是 0.2。</td></tr><tr><td style="text-align:left">minDistance</td><td style="text-align:left">相机距离焦点的最近距离，默认值是 0。</td></tr><tr><td style="text-align:left">maxDistance</td><td style="text-align:left">相机距离焦点的最远距离，默认值是 Infinity（无限远）。</td></tr></tbody></table></div><p>相对于 OrbitControls 控制器，TrackballControls 控制器的属性少一些，但是相关的功能还是比较全面的。TrackballControls 控制器的方法也和 OrbitControls 控制器的方法雷同。</p><h5 id="update-1"><a href="#update-1" class="headerlink" title="update()"></a><strong>update()</strong></h5><p>TrackballControls 控制器更新相机的方法，需要在每一帧里面调用。</p><h5 id="reset-1"><a href="#reset-1" class="headerlink" title="reset()"></a><strong>reset()</strong></h5><p>重置方法，相机回到初始位置。</p><h5 id="dispose-1"><a href="#dispose-1" class="headerlink" title="dispose()"></a><strong>dispose()</strong></h5><p>销毁当前实例化的 TrackballControls 控制器。</p><h5 id="change-回调-1"><a href="#change-回调-1" class="headerlink" title="change 回调"></a>change 回调</h5><p>我们还可以监听相机改变回调，如果控制器修改了相机，将会产生一个回调：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs javascript">controls.addEventListener(<span class="hljs-string">&#x27;change&#x27;</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>&#123;<br>    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;相机动了！&quot;</span>);<br>&#125;);<br></code></pre></td></tr></table></figure><p>最后，附上 TrackballControls 控制器案例：</p><ul><li><a href="https://johnson2heng.github.io/GitChat-Three.js/09第九节 controls/TrackballControls.html">点击这里</a></li></ul><p>也可以从这里获取案例源码：</p><ul><li><a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/09第九节 controls/TrackballControls.html">点击这里</a></li></ul><h3 id="DeviceOrientationControls"><a href="#DeviceOrientationControls" class="headerlink" title="DeviceOrientationControls"></a>DeviceOrientationControls</h3><p>最后，我们介绍的这个控制器只兼容含有陀螺仪的移动端。DeviceOrientationControls 控制器可以通过获取设备的陀螺仪状态来控制相机的朝向。</p><p>如果你还对陀螺仪不了解，<a href="https://blog.csdn.net/qq_30100043/article/details/73323617">请点击查看这里</a>，在这里不多说。</p><p>DeviceOrientationControls 的内容配置较少，我们先看一下案例。</p><p>使用手机打开网址：<a href="https://johnson2heng.github.io/GitChat-Three.js/09第九节 controls/DeviceOrientationControls.html">点击这里</a> ，然后手机朝下然后移动，你会发现能够通过手机的转向来控制相机的朝向，是不是很神奇。接下来我们看看如何引入到项目中。</p><ul><li>首先，将插件文件引入到项目中：</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;../js/DeviceOrientationControls.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br></code></pre></td></tr></table></figure><ul><li>然后，通过相机对象实例化相机：</li></ul><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-attr">control</span> = new THREE.DeviceOrientationControls(camera)<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><ul><li>最后，在每一帧渲染里面更新相机的位置：</li></ul><figure class="highlight mel"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs mel">function <span class="hljs-keyword">render</span>() &#123;<br>    <span class="hljs-keyword">control</span>.update();<br>    <span class="hljs-keyword">renderer</span>.<span class="hljs-keyword">render</span>(scene, <span class="hljs-keyword">camera</span>);<br>&#125;<br></code></pre></td></tr></table></figure><p>这样我们就完成了对 DeviceOrientationControls 控制器的添加。</p><p>DeviceOrientationControls 控制器相关的配置也很少，只有一个 Enabled 属性，设置为 True，则控制器会更新相机的位置，反之，设置 False 将无法更新相机位置。</p><p>还有一个方法就是销毁当前控制器的方法：</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs awk">controls.dispose(); <span class="hljs-regexp">//</span>销毁当前控制器<br></code></pre></td></tr></table></figure><p>最后，附上源码：</p><ul><li><a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/09第九节 controls/DeviceOrientationControls.html">点击这里</a></li></ul><h2 id="模型加载Loaders"><a href="#模型加载Loaders" class="headerlink" title="模型加载Loaders"></a>模型加载Loaders</h2><p>现在市面上的 3D 模型有上百种，每一种格式都有不同的用途，不同的功能和复杂程度。尽管 Three.js 提供了很多的加载器，但选择正确的格式和工作流程将为以后的工作节省大量时间和成本。而且某些格式难以使用，效率低下，甚至有些目前还未完全被支持。</p><h3 id="推荐使用的模型格式"><a href="#推荐使用的模型格式" class="headerlink" title="推荐使用的模型格式"></a>推荐使用的模型格式</h3><p>官方推荐我们使用的 3D 模型的格式为 glTF，由于 glTF 专注于传输，因此它的传输和解析的速度都很快。glTF 模型的功能包括网格、材质、纹理、蒙皮、骨骼、变形动画、骨骼动画、灯光以及相机。</p><p>如果当前的首选不是 glTF 格式，那么推荐使用 Three.js 定期维护并且流行的格式 FBX、OBJ 或者 COLLADA 格式，Three.js 也有自己独有的 JSON 格式。我们接下来将介绍这五种格式。</p><h3 id="Three-js-的-JSON-格式"><a href="#Three-js-的-JSON-格式" class="headerlink" title="Three.js 的 JSON 格式"></a>Three.js 的 JSON 格式</h3><p>这里的 JSON 格式指的是 Three.js 可以将其转换为场景 3D 对象的 JSON 格式模型。这种格式内部一般必有的四项为：</p><ul><li>metadata：当前模型的相关信息以及生成的工具信息；</li><li>geometries：存储当前模型所使用的几何体的数组；</li><li>materials：存储当前模型所使用的材质的数组；</li><li>object：当前模型的结构以及标示所应用到的材质和几何体标示。</li></ul><p>所有的模型网格，几何体和材质都有一个固定的 UUID 标识符，在 JSON 格式中均通过 UUID 引用。</p><h4 id="3D-对象转成-JSON"><a href="#3D-对象转成-JSON" class="headerlink" title="3D 对象转成 JSON"></a>3D 对象转成 JSON</h4><p>所有的 <code>THREE.Object3D</code> 对象都可以转成 JSON 字符串保存成为文件，但我们不能直接将对象转成 JSON，因为 JSON 无法保存函数。Three.js 给我们提供了一个 toJSON() 的方法来让我们将其转换为可存储的 JSON 格式。</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">var obj = scene.<span class="hljs-keyword">to</span><span class="hljs-constructor">JSON()</span>; <span class="hljs-comment">//将整个场景的内容转换成为 JSON 对象</span><br>var obj = group.<span class="hljs-keyword">to</span><span class="hljs-constructor">JSON()</span>; <span class="hljs-comment">//将一个模型组转成 JSON 对象</span><br>var obj = mesh.<span class="hljs-keyword">to</span><span class="hljs-constructor">JSON()</span>; <span class="hljs-comment">//将一个模型网格转成 JSON 对象</span><br>var JSONStr = <span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">JSON</span>.</span></span>stringify(obj); <span class="hljs-comment">//将 JSON 对象转换成 JSON 字符串</span><br></code></pre></td></tr></table></figure><p>按照这种方式，我们就可以将生成的场景模型保存为文件。</p><h4 id="使用-ObjectLoader-加载-JSON-模型"><a href="#使用-ObjectLoader-加载-JSON-模型" class="headerlink" title="使用 ObjectLoader 加载 JSON 模型"></a>使用 ObjectLoader 加载 JSON 模型</h4><p>这里我们将使用到 Three.js 内置的对象 <code>THREE.ObjectLoader</code> 加载模型。</p><p>直接加载 Three.js 生成的 JSON 对象，代码如下：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">var obj = scene.<span class="hljs-keyword">to</span><span class="hljs-constructor">JSON()</span>; <span class="hljs-comment">//将整个场景的内容转换成为json对象</span><br><br><span class="hljs-keyword">let</span> loader = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">ObjectLoader()</span>; <span class="hljs-comment">//实例化ObjectLoader对象</span><br><span class="hljs-keyword">let</span> scene = loader.parse(obj); <span class="hljs-comment">//将json对象再转换成3D对象</span><br></code></pre></td></tr></table></figure><p>加载外部的 JSON 文件：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs csharp"><span class="hljs-keyword">let</span> loader = <span class="hljs-keyword">new</span> THREE.ObjectLoader(); <span class="hljs-comment">//实例化ObjectLoader对象</span><br><br><span class="hljs-comment">//加载模型，并在回调中将生成的模型对象添加到场景中</span><br>loader.load(<span class="hljs-string">&quot;../js/models/json/file.json&quot;</span>, function (<span class="hljs-keyword">group</span>) &#123;<br>    scene.<span class="hljs-keyword">add</span>(<span class="hljs-keyword">group</span>);<br>&#125;);<br></code></pre></td></tr></table></figure><p>案例地址：<a href="https://johnson2heng.github.io/GitChat-Three.js/10第十节 loaders/ObjectLoader.html">请点击这里</a>。</p><p>案例的右上角有四个点击事件：</p><ul><li>添加模型：将在场景内随机生成一组立方体，每次都不相同。</li><li>导出模型：将场景内这一组立方体导出到本地 JSON 文件。</li><li>导入模型：可以选择将符合 JSON 文件作解析并导入到场景内。</li><li>加载模型：将加载服务器上面的一个 JSON 文件。</li></ul><p>案例代码地址：<a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/10第十节 loaders/ObjectLoader.html">请点击这里</a>。</p><h3 id="glTF-格式文件导入"><a href="#glTF-格式文件导入" class="headerlink" title="glTF 格式文件导入"></a>glTF 格式文件导入</h3><p>glTF 格式的 3D 格式文件是官方推荐的使用格式，这种格式的文件我们可以在 Sketchfab 官网下载，这是一个国外比较知名的模型网站。</p><ul><li>下载地址：<a href="https://sketchfab.com/models?date=week&amp;features=downloadable&amp;sort_by=-likeCount&amp;type=models">点击这里</a>。</li></ul><p>我们可以在这里下载一些免费的 glTF 格式的模型。</p><p>我在官网上找了个不错的模型做了一个案例，<a href="https://johnson2heng.github.io/GitChat-Three.js/10第十节 loaders/GLTFLoader.html">点击这里查看</a>。</p><img src="/blog/posts/ab44f639/c4b67350-8394-11e8-81ea-e357bbe10665" class="" title="gltf"><p>模型加载的速度会有些慢，大家可以等待下便能够看到这个小汽车。</p><p>接下来我们便讲解一下加载 glTF 模型的流程。</p><ul><li>首先，将 GLTFLoader 加载器插件引入到页面，插件在官方包的 <code>/examples/js/loaders/</code> 文件夹中，一些文件的导入插件都在这个文件夹内，大家有兴趣可以研究一下：</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;../js/loaders/GLTFLoader.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br></code></pre></td></tr></table></figure><ul><li>然后创建一个加载器：</li></ul><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs haxe"><span class="hljs-keyword">var</span> loader = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.GLTFLoader();<br></code></pre></td></tr></table></figure><ul><li>使用加载器加载模型，并调节一下模型大小在场景内展示：</li></ul><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs maxima">loader.<span class="hljs-built_in">load</span>(&#x27;../js/models/gltf/<span class="hljs-built_in">scene</span>.gltf&#x27;, function (gltf) &#123;<br>    gltf.<span class="hljs-built_in">scene</span>.<span class="hljs-built_in">scale</span>.set(.<span class="hljs-number">1</span>,.<span class="hljs-number">1</span>,.<span class="hljs-number">1</span>);<br>    <span class="hljs-built_in">scene</span>.add(gltf.<span class="hljs-built_in">scene</span>);<br>&#125;);<br></code></pre></td></tr></table></figure><p>有时候我们可能不明白，我加载了一个模型，哪一部分是需要导入场景的模型呢？</p><p>这里我们可以先将解析的出来的模型对象打印一下，然后通过查看对象属性来了解导入场景内的对象，就比如 glTF 模型转换出来的对象的 scene 属性就是需要导入场景的对象，而 JSON 格式的模型是直接可以导入的对象。</p><p>模型加载案例源码：<a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/10第十节 loaders/GLTFLoader.html">请点击这里</a>。</p><h3 id="FBX-模型导入"><a href="#FBX-模型导入" class="headerlink" title="FBX 模型导入"></a>FBX 模型导入</h3><p>FBX 最大的用途是，在诸如 Max、Maya、Softimage 等软件间进行模型、材质、动作和摄影机信息的互导，这样就可以发挥 Max 和 Maya 等软件的优势。可以说，FBX 是最好的互导方案。</p><img src="/blog/posts/ab44f639/e7ead4e0-8396-11e8-a90b-215c3565b75a" class="" title="fbx"><p>接下来我们看一个 FBX 模型导入的案例，是我在网上下载的一个 FBX 格式的模型，导入到场景内的效果：<a href="https://johnson2heng.github.io/GitChat-Three.js/10第十节 loaders/FBXLoader.html">请点击这里查看</a>。</p><p>接下来，我们看下它的实现过程。</p><p>首先我们需要导入 FBXLoader 插件，并且还需要额外增加一个解析二进制文件的插件 <code>inflate.min.js</code> ，不导入该文件的话，除了一些字符串存储的 FBX 格式，别的格式都会报错：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;../js/loaders/inflate.min.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;../js/loaders/FBXLoader.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br></code></pre></td></tr></table></figure><p>创建 FBX 加载器：</p><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs haxe"><span class="hljs-keyword">var</span> loader = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.FBXLoader();<br></code></pre></td></tr></table></figure><p>修改模型大小，并设置每个模型网格可以投射阴影：</p><figure class="highlight xquery"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs xquery">loader.load(<span class="hljs-string">&#x27;../js/models/fbx/file.fbx&#x27;</span>, <span class="hljs-keyword">function</span> (fbx) &#123;<br>    fbx.scale.set(.<span class="hljs-number">1</span>,.<span class="hljs-number">1</span>,.<span class="hljs-number">1</span>);<br>    fbx.traverse(<span class="hljs-keyword">function</span> (<span class="hljs-type">item</span>) &#123;<br>       <span class="hljs-keyword">if</span>(<span class="hljs-type">item</span> instanceof THREE.Mesh)&#123;<br>           <span class="hljs-type">item</span>.castShadow =<span class="hljs-built_in"> true</span>;<br>           <span class="hljs-type">item</span>.receiveShadow =<span class="hljs-built_in"> true</span>;<br>       &#125;<br>    &#125;);<br>    scene.add(fbx);<br>&#125;);<br></code></pre></td></tr></table></figure><p>这样就实现了 FBX 模型的导入。</p><p>案例源码地址：<a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/10第十节 loaders/FBXLoader.html">请点击这里</a>。</p><h3 id="OBJ-格式模型导入"><a href="#OBJ-格式模型导入" class="headerlink" title="OBJ 格式模型导入"></a>OBJ 格式模型导入</h3><p>OBJ 文件是 3D 模型文件格式。由 Alias|Wavefront 公司为 3D 建模和动画软件 Advanced Visualizer 开发的一种标准，适合用于 3D 软件模型之间的互导，也可以通过 Maya 读写。</p><p>OBJ 文件是一种文本文件，可以直接用写字板打开进行查看和编辑修改，但不包含动画、材质特性、贴图路径、动力学、粒子等信息。</p><p>OBJ 文件的导出通常会和 MTL 格式一同导出，MTL 作为 OBJ 文件的附属文件，却有着 OBJ 文件需要的贴图材质，所以，我们通常使用时，将它们两个文件一同导入。</p><img src="/blog/posts/ab44f639/f0138980-8398-11e8-81ea-e357bbe10665" class="" title="obj"><p>这是我使用官网提供的一个模型制作的一个案例，查看地址：<a href="https://johnson2heng.github.io/GitChat-Three.js/10第十节 loaders/OBJLoader.html">请点击这里</a>。</p><p>我们看下实现导入的过程。</p><p>首先，我们需要将 OBJLoader 插件和 MTLLoader 插件引入页面：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;../js/loaders/OBJLoader.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;../js/loaders/MTLLoader.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br></code></pre></td></tr></table></figure><p>实例化 MTLLoader ：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs reasonml"><span class="hljs-comment">//创建MTL加载器</span><br>var mtlLoader = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">MTLLoader()</span>;<br><span class="hljs-comment">//设置文件路径</span><br>mtlLoader.set<span class="hljs-constructor">Path(&#x27;..<span class="hljs-operator">/</span><span class="hljs-params">js</span><span class="hljs-operator">/</span><span class="hljs-params">models</span><span class="hljs-operator">/</span><span class="hljs-params">obj</span><span class="hljs-operator">/</span>&#x27;)</span>;<br></code></pre></td></tr></table></figure><p>如果有需要，我们还可以设置纹理文件夹地址：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs reasonml"><span class="hljs-comment">//设置纹理文件路径</span><br>mtlLoader.set<span class="hljs-constructor">TexturePath(&#x27;..<span class="hljs-operator">/</span><span class="hljs-params">js</span><span class="hljs-operator">/</span><span class="hljs-params">models</span><span class="hljs-operator">/</span><span class="hljs-params">obj</span><span class="hljs-operator">/</span>&#x27;)</span>;<br></code></pre></td></tr></table></figure><p>加载 MTL 文件，并在文件加载成功后，创建 OBJLoader 并设置对象应用当前的材质：</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs php"><span class="hljs-comment">//加载mtl文件</span><br>mtlLoader.load(<span class="hljs-string">&#x27;female02.mtl&#x27;</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">material</span>) </span>&#123;<br>    <span class="hljs-comment">//创建OBJ加载器</span><br>    <span class="hljs-keyword">var</span> objLoader = <span class="hljs-keyword">new</span> THREE.OBJLoader();<br>    <span class="hljs-comment">//设置当前加载的纹理</span><br>    objLoader.setMaterials(material);<br>    objLoader.setPath(<span class="hljs-string">&#x27;../js/models/obj/&#x27;</span>);<br>    objLoader.load(<span class="hljs-string">&#x27;female02.obj&#x27;</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"><span class="hljs-keyword">object</span></span>) </span>&#123;<br>        <span class="hljs-comment">//添加阴影</span><br>        <span class="hljs-keyword">object</span>.traverse(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">item</span>) </span>&#123;<br>            <span class="hljs-keyword">if</span>(item <span class="hljs-keyword">instanceof</span> THREE.Mesh)&#123;<br>                item.castShadow = <span class="hljs-literal">true</span>;<br>                item.receiveShadow = <span class="hljs-literal">true</span>;<br>            &#125;<br>        &#125;);<br>        <span class="hljs-comment">//缩放</span><br>        <span class="hljs-keyword">object</span>.scale.set(<span class="hljs-number">.3</span>,<span class="hljs-number">.3</span>,<span class="hljs-number">.3</span>);<br>        scene.add(<span class="hljs-keyword">object</span>);<br>    &#125;)<br>&#125;);<br></code></pre></td></tr></table></figure><p>我们再去加载 OBJ 文件，加载成功的文件就是可以导入到场景内的 3D 对象。</p><p>案例源码查看地址：<a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/10第十节 loaders/OBJLoader.html">请点击这里</a>。</p><h3 id="COLLADA-模型导入"><a href="#COLLADA-模型导入" class="headerlink" title="COLLADA 模型导入"></a>COLLADA 模型导入</h3><p>COLLADA 是一个开放的标准，最初用于 3D 软件数据交换，由 SCEA 发起，现在则被许多著名厂家（如 Autodesk、XSI 等）支持。COLLADA 不仅仅可以用于建模工具之间的数据交换，也可以作为场景描述语言用于小规模的实时渲染。</p><p>COLLADA DOM 拥有丰富的内容用于表现场景中的各种元素，从多边形几何体到摄像机无所不包。我们可以通过 COLLADA DOM 库来进行场景文件的读取与处理操作。</p><img src="/blog/posts/ab44f639/88b97d60-839a-11e8-884a-35c5e2ab8361" class="" title="collada"><p>上面是我写的一个模型导入案例，案例地址：<a href="https://johnson2heng.github.io/GitChat-Three.js/10第十节 loaders/ColladaLoader.html">请点击这里</a>。</p><p>我们看下实现步骤。</p><p>首先引入 ColladaLoader 插件：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;../js/loaders/ColladaLoader.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br></code></pre></td></tr></table></figure><p>接着实例化 ColladaLoader 对象：</p><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs haxe"><span class="hljs-keyword">var</span> loader = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.ColladaLoader();<br></code></pre></td></tr></table></figure><p>最后加载文件并调整文件大小，添加到场景内：</p><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs actionscript">loader.load(<span class="hljs-string">&#x27;../js/models/collada/elf.dae&#x27;</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(collada)</span> </span>&#123;<br><br>    <span class="hljs-comment">//添加阴影</span><br>    collada.scene.traverse(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(item)</span> </span>&#123;<br>        <span class="hljs-keyword">if</span>(item <span class="hljs-keyword">instanceof</span> THREE.Mesh)&#123;<br>            item.castShadow = <span class="hljs-literal">true</span>;<br>            item.receiveShadow = <span class="hljs-literal">true</span>;<br>        &#125;<br>    &#125;);<br>    <span class="hljs-comment">//缩放</span><br>    collada.scene.scale.set(<span class="hljs-number">5</span>,<span class="hljs-number">5</span>,<span class="hljs-number">5</span>);<br>    scene.add(collada.scene);<br>&#125;);<br></code></pre></td></tr></table></figure><p>案例源码查看：<a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/10第十节 loaders/ColladaLoader.html">请点击这里</a>。</p><h3 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h3><p><strong>1. 如何知道，加载完成的模型需要将哪部分导入到场景？</strong></p><p>一般情况下都是将自身导入，比如 FBX，OBJ，JSON 等，还有一种，会在里面生成一个可导入 scene 属性，如 glTF 和 COLLADA 文件。如果导入哪部分你无法确定，你可以把模型对象打印到控制台查看，然后尝试往场景内导入。</p><p><strong>2.导入到场景内的模型无法查看，而且也没有报错，为什么？</strong></p><p>这种情况可能由多种情况造成的，一般主要有下面两种情况：</p><ul><li>模型太小或者太大，这种情况可以尝试放大一千倍或者缩小一千倍来查看效果。</li><li>模型的位置太偏，根本不在相机照射范围内，这种问题我们可以将模型居中到相机照射的焦点位置查看，如何居中我们将在后面的课中讲解。</li></ul><h2 id="Three-js-动画"><a href="#Three-js-动画" class="headerlink" title="Three.js 动画"></a>Three.js 动画</h2><p>动画一般可以分为两种：一种是变形动画，另一种是骨骼动画。下面，我们先介绍一下变形动画。</p><h3 id="变形动画"><a href="#变形动画" class="headerlink" title="变形动画"></a>变形动画</h3><p>变形动画，通过修改当前模型的顶点位置来实现。比如，一个动画需要变动十次才可以实现，那么我们需要为当前模型的每一个顶点定义每一次所在的位置，Three.js 通过这一次次的修改实现了动画的整个流程。</p><p>为了帮助大家更好地理解变形动画的实现与使用，我创建了一个案例，查看地址为：<a href="https://johnson2heng.github.io/GitChat-Three.js/11第十一节 animation/morphTargets.html">点击这里</a>。</p><p>在这个案例的右上角，我们能发现两个可切换的拖拽条。这两个拖拽条对应的是两个变形目标数组，拖拽范围是0-1，即当前的变形目标对本体的影响程度。拖拽它们，可发现界面中的立方体也会跟随之变动，从而影响当前的立方体。接下来我讲解一下，该案例的实现过程。</p><p>首先，创建模型的几何体，并为几何体 morphTargets 赋值两个变形目标。morphTargets 是一个数组，我们可以为其增加多个变形目标。在给 morphTargets 添加变形目标时，需要为其定义一个名称和相关的顶点，这个顶点数据必须和默认的模型的顶点数据保持一致，设置完后，我们需要调用 geometry 的 <code>computeMorphNormals()</code> 进行更新，代码如下：</p><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs haxe"><span class="hljs-keyword">var</span> cubeGeometry = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.BoxGeometry(<span class="hljs-number">4</span>, <span class="hljs-number">4</span>, <span class="hljs-number">4</span>);<br><br><span class="hljs-comment">// 创建两个影响立方体的变形目标</span><br><span class="hljs-keyword">var</span> cubeTarget1 = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.BoxGeometry(<span class="hljs-number">2</span>, <span class="hljs-number">10</span>, <span class="hljs-number">2</span>);<br><span class="hljs-keyword">var</span> cubeTarget2 = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.BoxGeometry(<span class="hljs-number">8</span>, <span class="hljs-number">2</span>, <span class="hljs-number">8</span>);<br><br><span class="hljs-comment">// 将两个geometry的顶点放入到立方体的morphTargets里面</span><br>cubeGeometry.morphTargets[<span class="hljs-number">0</span>] = &#123;name: <span class="hljs-type"></span>&#x27;target1<span class="hljs-string">&#x27;, vertices: cubeTarget1.vertices&#125;;</span><br><span class="hljs-string">cubeGeometry.morphTargets[1] = &#123;name: &#x27;</span>target2<span class="hljs-string">&#x27;, vertices: cubeTarget2.vertices&#125;;</span><br><span class="hljs-string">cubeGeometry.computeMorphNormals();</span><br></code></pre></td></tr></table></figure><p>然后，为当前模型设置材质，变形目标作为参数之一，可以使其变形。</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">var cubeMaterial = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">MeshLambertMaterial(&#123;<span class="hljs-params">morphTargets</span>: <span class="hljs-params">true</span>, <span class="hljs-params">color</span>: 0x00ffff&#125;)</span>;<br></code></pre></td></tr></table></figure><p>接着，将创建好的网格模型添加到场景中。这时可以在 mesh 对象中找到 morphTargetInfluences 配置项，它也是一个数组，和 geometry 的 morphTargets 相对应，主要用来设置当前变形目标对本体的影响度，默认值为0-1，0为不影响本体，1为完全影响本体：</p><figure class="highlight openscad"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs openscad">gui = &#123;<br>    influence1:<span class="hljs-number">0.01</span>,<br>    influence2:<span class="hljs-number">0.01</span>,<br>    update : <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> &#123;</span><br>        <span class="hljs-built_in">cube</span>.morphTargetInfluences[<span class="hljs-number">0</span>] = gui.influence1;<br>        <span class="hljs-built_in">cube</span>.morphTargetInfluences[<span class="hljs-number">1</span>] = gui.influence2;<br>    &#125;<br>&#125;;<br></code></pre></td></tr></table></figure><p>至此，我们就手动实现了一个变形动画。在这个过程中，我们发现，变形动画是由于不断修改变形目标对本体的影响度而产生的。我们可以通过这个原理实现其他变形动画。</p><p>案例代码查看地址：<a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/11第十一节 animation/morphTargets.html">请点击这里</a>。</p><h3 id="骨骼动画"><a href="#骨骼动画" class="headerlink" title="骨骼动画"></a>骨骼动画</h3><p>实现骨骼动画，我们需要生成一个与模型相关的骨架。骨架中的骨骼与骨骼之间存在关联，模型的每一个要动的顶点需要设置影响它的骨骼以及骨骼对顶点的影响度。</p><p>和变形动画相比，骨骼动画更复杂一些，但又有更多的灵活性。使用变形动画，我们需要把所有的每一次的变动都存在一个顶点数组中，而骨骼动画，只需要设置骨骼的相关信息，就可以实现更多的动画。</p><p>下面我们看一个骨骼动画的简单案例：<a href="https://johnson2heng.github.io/GitChat-Three.js/11第十一节 animation/skeleton.html">点击这里</a></p><p><img src="https://images.gitbook.cn/5411e080-8da8-11e8-b093-3f5eaa2cb071" alt="skeleton"></p><p>这是官方提供的一个案例。我对其做了些简单修改，以显示出当前一个柱形图形的骨骼。实现起来比较复杂，我们需要先理解它是怎么实现的。</p><p>首先， 我们创建了一个圆柱几何体，通过圆柱的几何体每个顶点的 y 轴坐标位置来设置绑定的骨骼的下标和影响的程度：</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs maxima">//遍历几何体所有的顶点<br><span class="hljs-keyword">for</span> (<span class="hljs-built_in">var</span> i = <span class="hljs-number">0</span>; i &lt; geometry.<span class="hljs-built_in">vertices</span>.<span class="hljs-built_in">length</span>; i++) &#123;<br><br>    //根据顶点的位置计算出骨骼影响下标和权重<br><br>    <span class="hljs-built_in">var</span> vertex = geometry.<span class="hljs-built_in">vertices</span>[i];<br>    <span class="hljs-built_in">var</span> y = (vertex.y + sizing.halfHeight);<br><br>    <span class="hljs-built_in">var</span> skinIndex = Math.<span class="hljs-built_in">floor</span>(y / sizing.segmentHeight);<br>    <span class="hljs-built_in">var</span> skinWeight = (y <span class="hljs-symbol">%</span> sizing.segmentHeight) / sizing.segmentHeight;<br><br>    geometry.skinIndices.<span class="hljs-built_in">push</span>(<span class="hljs-built_in">new</span> THREE.Vector4(skinIndex, skinIndex + <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>));<br>    geometry.skinWeights.<span class="hljs-built_in">push</span>(<span class="hljs-built_in">new</span> THREE.Vector4(<span class="hljs-number">1</span> - skinWeight, skinWeight, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>));<br><br>&#125;<br></code></pre></td></tr></table></figure><p>几何体的 skinIndices 属性和 skinWeights 属性分别用来设置绑定的骨骼下标和权重（骨骼影响程度）。</p><p>相应的，我们需要一组相关联的骨骼。骨骼具有嵌套关系，才得以实现一个骨架。圆柱体比较简单，我们直接创建一条骨骼垂直嵌套的骨骼：</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs abnf"><span class="hljs-attribute">bones</span> = []<span class="hljs-comment">;</span><br><br>var prevBone = new THREE.Bone()<span class="hljs-comment">;</span><br>bones.push(prevBone)<span class="hljs-comment">;</span><br>prevBone.position.y = -sizing.halfHeight<span class="hljs-comment">;</span><br><br>for (var i = <span class="hljs-number">0</span><span class="hljs-comment">; i &lt; sizing.segmentCount; i++) &#123;</span><br><br>    var bone = new THREE.Bone()<span class="hljs-comment">;</span><br>    bone.position.y = sizing.segmentHeight<span class="hljs-comment">;</span><br>    bones.push(bone)<span class="hljs-comment">; //添加到骨骼数组</span><br>    prevBone.add(bone)<span class="hljs-comment">; //上一个骨骼定义为父级</span><br>    prevBone = bone<span class="hljs-comment">;</span><br><br>&#125;<br></code></pre></td></tr></table></figure><p>创建纹理时，我们还需要设置当前材质属性，并开启骨骼动画对其的修改权限，将材质的 skinning 属性设置为 true：</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs maxima"><span class="hljs-built_in">var</span> lineMaterial = <span class="hljs-built_in">new</span> THREE.MeshBasicMaterial(&#123;<br>    skinning: <span class="hljs-literal">true</span>,<br>    <span class="hljs-built_in">wireframe</span>: <span class="hljs-literal">true</span><br>&#125;);<br></code></pre></td></tr></table></figure><p>最后，我们需要创建骨骼材质，并将模型绑定骨骼：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">mesh = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">SkinnedMesh(<span class="hljs-params">geometry</span>, [<span class="hljs-params">material</span>, <span class="hljs-params">lineMaterial</span>])</span>;<br>var skeleton = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">Skeleton(<span class="hljs-params">bones</span>)</span>; <span class="hljs-comment">//创建骨架</span><br>mesh.add(bones<span class="hljs-literal">[<span class="hljs-number">0</span>]</span>); <span class="hljs-comment">//将骨骼添加到模型里面</span><br>mesh.bind(skeleton); <span class="hljs-comment">//模型绑定骨架</span><br></code></pre></td></tr></table></figure><p>这样，我们就使用 Three.js 创建了一个简单的骨骼动画。使用 <code>dat.gui</code>，便于我们修改每一个骨骼的 poisition、rotation 和 scale 并查看对当前模型的影响。</p><p>案例的源码地址：<a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/11第十一节 animation/skeleton.html">点击这里</a>。</p><h3 id="两种动画的区别"><a href="#两种动画的区别" class="headerlink" title="两种动画的区别"></a>两种动画的区别</h3><p>变形动画主要用于精度要求高的动画，比如人物的面部表情。其优点是动画的展现效果很到位，缺点就是扩展性不强，只能执行设置好的相关动画。</p><p>骨骼动画主要用于精度要求相对低一些，但需要丰富多样的动画的场合，就比如人物的走动，攻击防御等动画，我们可以通过一套骨骼，修改相应骨骼的位置信息直接实现相应的效果。它没有变形动画的精度高，但可以实现多种多样的效果。</p><blockquote><p><strong>总结：</strong> 我们可以根据项目的需求来设置不同的动画，就比如一个人物模型，说话我们使用变形动画去实现，而肢体动作使用骨骼动画去实现。</p></blockquote><h3 id="导入模型动画"><a href="#导入模型动画" class="headerlink" title="导入模型动画"></a>导入模型动画</h3><p>在 Three.js 动画系统中，你可以为模型的各种属性设置动画，如骨骼动画，变形动画，以及材质的相关属性（颜色，透明度， 是否可见）。动画属性可以设置淡入淡出效果以及各种扭曲特效，也可以单独改变一个或多个对象上的动画影响程度和动画时间。</p><p>为了实现这些，Three.js 动画系统在2015年修改为了类似于 Unity 和虚幻引擎4的架构。接下来我们了解下这套动画系统的主要组件以及它们是如何协同工作的。</p><h4 id="动画片段（Animation-Clips）"><a href="#动画片段（Animation-Clips）" class="headerlink" title="动画片段（Animation Clips）"></a>动画片段（Animation Clips）</h4><p>在我们成功导入模型以后，如果模型拥有相关的动画属性，会在返回的模型数据中产生一个名为 animations 的数组，数组的每一个子项都是一个 AnimationClips 对象。</p><p>每一个单独 AnimationClips 对象相应的保存着模型的一个动画的数据，假如，如果模型网格是一个人物角色，第一个 AnimationClips 对象有可能保存的是人物走动的动画，第二个 AnimationClips 对象用于跳跃，第三个用于攻击动画等等。</p><h4 id="关键帧轨迹（Keyframe-Tracks）"><a href="#关键帧轨迹（Keyframe-Tracks）" class="headerlink" title="关键帧轨迹（Keyframe Tracks）"></a>关键帧轨迹（Keyframe Tracks）</h4><p>在 AnimationClips 对象内部，一般有四个属性：</p><ul><li>name：当前动画的名称；</li><li>uuid：一个不会重复的 uuid；</li><li>duration：当前动画一个循环所需要的时间；</li><li>tracks：轨迹，即当前动画每一次切换动作所需要的数据。</li></ul><p>假设当前的动画是骨骼动画，在关键帧轨迹中存储的数据是每一帧骨骼随着时间变动的数据（位置，旋转和缩放等）。</p><p>如果当前动画是一个变形动画，在关键帧轨迹中将会把顶点数据的变动存储在其中（比如实现人脸的笑以及哭等动作）。</p><h4 id="动画混合器（Animation-Mixer）"><a href="#动画混合器（Animation-Mixer）" class="headerlink" title="动画混合器（Animation Mixer）"></a>动画混合器（Animation Mixer）</h4><p>在动画片段中存储的数据仅仅构成了动画实现的基础，实际的播放权力在动画混合器的手中。你可以想象动画混合器其实不仅仅只是作为动画的播放器，它还可以同时控制几个动画，混合它们或者合并它们。</p><h4 id="动画播放器（Animation-Actions）"><a href="#动画播放器（Animation-Actions）" class="headerlink" title="动画播放器（Animation Actions）"></a>动画播放器（Animation Actions）</h4><p>这个英文我更乐意将它翻译成动画播放器，因为我们最终需要将数据生成一个动画播放器来操作当前的动画执行，暂停或者停止，是否使用淡入淡出效果或者将动画加快或减慢。</p><h4 id="动画对象组（Animation-Object-Groups）"><a href="#动画对象组（Animation-Object-Groups）" class="headerlink" title="动画对象组（Animation Object Groups）"></a>动画对象组（Animation Object Groups）</h4><p>如果你希望一组模型对象共享当前的动画，我们可以使用动画对象组来实现。</p><h3 id="通过导入模型显示动画"><a href="#通过导入模型显示动画" class="headerlink" title="通过导入模型显示动画"></a>通过导入模型显示动画</h3><img src="/blog/posts/ab44f639/316ee240-8e92-11e8-bfb3-8b3f110a427d" class="" title="变形动画"><h4 id="变形动画-1"><a href="#变形动画-1" class="headerlink" title="变形动画"></a>变形动画</h4><p>我们首先查看一个官方的模型案例，这个案例是一匹马奔跑的动画，我们也可以通过下面地址查看：<a href="https://johnson2heng.github.io/GitChat-Three.js/11第十一节 animation/morphAnimation.html">点击这里</a>。</p><p>接下来我们看一下这匹马是如何实现的。</p><ul><li>在模型加载成功以后，我们首先将模型创建出来，并将材质的 morphTargets 设置为 ture，使顶点数据信息可以受变形动画影响：</li></ul><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs maxima"><span class="hljs-built_in">mesh</span> = <span class="hljs-built_in">new</span> THREE.Mesh(geometry, <span class="hljs-built_in">new</span> THREE.MeshLambertMaterial(&#123;<br>    vertexColors: THREE.FaceColors,<br>    morphTargets: <span class="hljs-literal">true</span><br>&#125;));<br><span class="hljs-built_in">mesh</span>.castShadow = <span class="hljs-literal">true</span>;<br><span class="hljs-built_in">mesh</span>.<span class="hljs-built_in">scale</span>.set(<span class="hljs-number">0.1</span>, <span class="hljs-number">0.1</span>, <span class="hljs-number">0.1</span>);<br><span class="hljs-built_in">scene</span>.add(<span class="hljs-built_in">mesh</span>);<br></code></pre></td></tr></table></figure><ul><li>然后我们创建了一个针对于该模型的混合器：</li></ul><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-attr">mixer</span> = new THREE.AnimationMixer(mesh)<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><ul><li>接着使用变形目标数据创建一个动画片段：</li></ul><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">var clip = THREE.AnimationClip.<span class="hljs-constructor">CreateFromMorphTargetSequence(&#x27;<span class="hljs-params">gallop</span>&#x27;, <span class="hljs-params">geometry</span>.<span class="hljs-params">morphTargets</span>, 30)</span>;<br></code></pre></td></tr></table></figure><ul><li>使用混合器和动画片段创建一个动画播放器来播放：</li></ul><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs awk">var action = mixer.clipAction(clip); <span class="hljs-regexp">//</span>创建动画播放器<br>action.setDuration(<span class="hljs-number">1</span>); <span class="hljs-regexp">//</span>设置当前动画一秒为一个周期<br>action.play(); <span class="hljs-regexp">//</span>设置当前动画播放<br></code></pre></td></tr></table></figure><ul><li>最后，我们还需要在重新绘制循环中更新混合器，进行动作更新：</li></ul><figure class="highlight mel"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs mel">function <span class="hljs-keyword">render</span>() &#123;<br><br>    <span class="hljs-keyword">control</span>.update();<br><br>    var time = clock.getDelta();<br>    <span class="hljs-comment">//由于模型导入是异步的，所以我们再模型没有加载完之前是获取不到混合器的</span><br>    <span class="hljs-keyword">if</span> (mixer) &#123;<br>        mixer.update(time);<br>    &#125;<br><br>    <span class="hljs-keyword">renderer</span>.<span class="hljs-keyword">render</span>(scene, <span class="hljs-keyword">camera</span>);<br>&#125;<br></code></pre></td></tr></table></figure><img src="/blog/posts/ab44f639/27172660-8e95-11e8-8ee0-a17ea463076e" class="" title="骨骼动画"><h4 id="骨骼动画-1"><a href="#骨骼动画-1" class="headerlink" title="骨骼动画"></a>骨骼动画</h4><p>骨骼动画模型，我们使用的是 gltf 格式，这个模型是在 Sketchfab 网站下载的，案例是一个小姐姐跳舞的片段，查看地址：<a href="https://johnson2heng.github.io/GitChat-Three.js/11第十一节 animation/skeletonAnimation.html">点击这里</a>。</p><p>gltf 格式的模型导入进来后，我们可以直接通过 animations 数组创建播放器：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">mixer = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">AnimationMixer(<span class="hljs-params">obj</span>)</span>; <span class="hljs-comment">//通过当前模型创建混合器</span><br>action = mixer.clip<span class="hljs-constructor">Action(<span class="hljs-params">gltf</span>.<span class="hljs-params">animations</span>[0])</span>; <span class="hljs-comment">//通过动画数据创建播放器</span><br></code></pre></td></tr></table></figure><p>直接调用播放器的播放事件让动画播放：</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs abnf">action.play()<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><p>最后，我们还需要在循环渲染中更新混合器，并将每一帧渲染的间隔时间传入：</p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs lua"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">render</span><span class="hljs-params">()</span></span> &#123;<br>    control.update();<br>    var <span class="hljs-built_in">time</span> = <span class="hljs-built_in">clock</span>.getDelta();<br>    <span class="hljs-keyword">if</span> (mixer) &#123;<br>        mixer.update(<span class="hljs-built_in">time</span>);<br>    &#125;<br>    renderer.render(scene, camera);<br>&#125;<br></code></pre></td></tr></table></figure><h2 id="Tween-js-补间动画"><a href="#Tween-js-补间动画" class="headerlink" title="Tween.js 补间动画"></a>Tween.js 补间动画</h2><h3 id="Tween-是什么"><a href="#Tween-是什么" class="headerlink" title="Tween 是什么"></a>Tween 是什么</h3><p>Tween.js 是 JavaScript 中一个简单的补间动画库，包含各种经典动画算法。Tween.js 支持数字对象的属性和 CSS 样式属性赋值，API 简单且强大，支持链式调用。</p><p>补间（动画）（来自 In-Between）是一个概念，允许你以平滑的方式更改对象的属性。你只需告诉它哪些属性要更改，当补间结束运行时它们应该具有哪些最终值，以及这个过程需要多长时间，补间引擎将负责计算从起始点到结束点的值。</p><p>在 Three.js 中，我们有一些修改模型位置，旋转和缩放的需求，却无法直接在 WebGL 中使用 CSS3 动画来实现，而 Tween.js 恰好给我们提供了一个很好的解决方案。</p><p>比如我们要实现一个模型从 A 点到 B 点的位置移动，常规的实现方法，是使用 setInterval、requestAnimationFrame 手动计算出特定时间的位置点，很不易于管理与查看。而 Tween.js 可以自动根据起始点位置和动画时长计算出所有的位置点，可以很方便地对其进行获取和管理。</p><h4 id="简单应用"><a href="#简单应用" class="headerlink" title="简单应用"></a>简单应用</h4><p>接下来，我们通过一个案例，带大家了解如何在 Three.js 应用中使用 Tween.js。</p><blockquote><p>案例 Demo 查看地址：<a href="https://johnson2heng.github.io/GitChat-Three.js/12第十二节 tween/simple.html">点击这里</a>。</p><p>案例代码查看地址：<a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/12第十二节 tween/simple.html">点击这里</a>。</p></blockquote><p>本案例的开发思路是：首先获取目标模型的初始位置，然后实例化 Tween，接着设置目标位置，启动 Tween，在 TWEEN.onUpdate() 回调中改变目标模型的位置，从而实现目标模型从初始位置平滑移动到目标位置的动画。</p><p>实现代码如下：</p><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs arduino"><span class="hljs-comment">//设置tween</span><br>       var position = &#123;x:<span class="hljs-number">-40</span>, y:<span class="hljs-number">0</span>, z:<span class="hljs-number">-30</span>&#125;;<br>       tween = <span class="hljs-keyword">new</span> TWEEN.<span class="hljs-built_in">Tween</span>(position);<br>       <span class="hljs-comment">//设置移动的目标和移动时间</span><br>       tween.<span class="hljs-built_in">to</span>(&#123;x:<span class="hljs-number">40</span>, y:<span class="hljs-number">30</span>, z:<span class="hljs-number">30</span>&#125;, <span class="hljs-number">2000</span>);<br>       <span class="hljs-comment">//设置每次更新的回调，然后修改几何体的位置</span><br>       tween.<span class="hljs-built_in">onUpdate</span>(<span class="hljs-built_in">function</span> (pos) &#123;<br>           cube.position.<span class="hljs-built_in">set</span>(pos.x, pos.y, pos.z);<br>       &#125;);<br></code></pre></td></tr></table></figure><p>上面代码，首先创建一个 position 对象，存储了当前立方体的位置数据。然后，通过当前的对象创建了一个补间 tween。紧接着，设置每一个属性的目标位置，并告诉 Tween 在 2000 毫秒（动画时长）内移动到目标位置。最后，设置 Tween 对象每次更新的回调，即在每次数据更新以后，将立方体位置更新。</p><p>Tween 对象不会直接执行，需要我们调用 <code>start()</code> 方法激活，即 <code>tween.start()</code>。</p><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs actionscript"><span class="hljs-comment">//声明一个保存需求修改的数据对象。</span><br>gui = &#123;<br>    start:<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">()</span> </span>&#123;<br>        tween.start();<br>    &#125;<br>&#125;;<br></code></pre></td></tr></table></figure><p>想要完成整个过程，我们还需要在每帧里面调用 <code>TWEEN.update</code>，来触发 Tween 对象更新位置：</p><figure class="highlight mel"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs mel">function <span class="hljs-keyword">render</span>() &#123;<br><br>    <span class="hljs-comment">//更新Tween</span><br>    TWEEN.update();<br><br>    <span class="hljs-keyword">control</span>.update();<br><br>    <span class="hljs-keyword">renderer</span>.<span class="hljs-keyword">render</span>(scene, <span class="hljs-keyword">camera</span>);<br>&#125;<br></code></pre></td></tr></table></figure><h4 id="链式调用"><a href="#链式调用" class="headerlink" title="链式调用"></a>链式调用</h4><p>链式调用可以简化大量代码，逻辑清晰集中，便于查看和修改。</p><p>Tween 插件也支持链式调用的方法，并且还会修改实例化时传入的对象，如下代码：</p><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs arduino"><span class="hljs-comment">//设置tween</span><br>var position = &#123;x:<span class="hljs-number">-40</span>, y:<span class="hljs-number">0</span>, z:<span class="hljs-number">-30</span>&#125;;<br>tween = <span class="hljs-keyword">new</span> TWEEN.<span class="hljs-built_in">Tween</span>(position);<br><br><span class="hljs-comment">//设置移动的目标和移动时间</span><br>tween.<span class="hljs-built_in">to</span>(&#123;x:<span class="hljs-number">40</span>, y:<span class="hljs-number">30</span>, z:<span class="hljs-number">30</span>&#125;, <span class="hljs-number">2000</span>);<br><br><span class="hljs-comment">//设置每次更新的回调，然后修改几何体的位置</span><br>tween.<span class="hljs-built_in">onUpdate</span>(<span class="hljs-built_in">function</span> (pos) &#123;<br>    cube.position.<span class="hljs-built_in">set</span>(pos.x, pos.y, pos.z);<br>&#125;);<br></code></pre></td></tr></table></figure><p>可以简化为链式调用：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs reasonml"><span class="hljs-comment">//直接链式实现tween</span><br>tween = <span class="hljs-keyword">new</span> TWEEN.<span class="hljs-constructor">Tween(<span class="hljs-params">cube</span>.<span class="hljs-params">position</span>)</span>.<span class="hljs-keyword">to</span>(&#123;x:<span class="hljs-number">40</span>, y:<span class="hljs-number">30</span>, z:<span class="hljs-number">30</span>&#125;, <span class="hljs-number">2000</span>);<br></code></pre></td></tr></table></figure><h3 id="Tween-对象方法"><a href="#Tween-对象方法" class="headerlink" title="Tween 对象方法"></a>Tween 对象方法</h3><h4 id="控制动画方法"><a href="#控制动画方法" class="headerlink" title="控制动画方法"></a>控制动画方法</h4><p>Tween 对象控制动画的方法主要包括开始、取消、重复等方法。</p><ul><li><code>.start()</code></li></ul><p>如果你想激活一个补间，请使用这个方法，调用方式如下所示：</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs abnf">tween.start()<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><p><code>start()</code> 方法还接受一个时间参数，添加该参数后，补间不会立即被激活，Tween 动画将在延时该时间数后才开始动画。否则它将立刻开始动画，且在第一次调用 <code>TWEEN.update</code> 时开始计时。如果设置的时间已经小于计时的总时间，那计算出来的位置数据将是参数设置时间开始后，运行到的所在位置。</p><ul><li><code>.stop()</code></li></ul><p>这个方法刚好和 <code>start()</code> 方法对应，如果你想取消一个补间，直接调用这个方法即可：</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs abnf">tween.stop()<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><ul><li><code>.update()</code></li></ul><p>其实每个补间都有一个更新方法，只不过我们多会使用 <code>TWEEN.update</code> ，而不会单独调用它（见下方全局函数）。</p><ul><li><code>.chain()</code></li></ul><p>当你按顺序排列不同的补间时，例如当上一个补间结束时立即启动另外一个补间，我们称它为链式补间。关于链式补间的案例请见下面两个链接。</p><blockquote><p>案例 Demo 查看地址：<a href="https://johnson2heng.github.io/GitChat-Three.js/12第十二节 tween/chain.html">点击这里</a></p><p>案例代码查看地址：<a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/12第十二节 tween/chain.html">点击这里</a></p></blockquote><p>调用方法为：</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs abnf">tweenA.chain(tweenB)<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><p>或者，采用一个无限的链式，即 tweenA 与 tweenB 无限循环，便可以写成：</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs abnf">tweenA.chain(tweenB)<span class="hljs-comment">;</span><br>tweenB.chain(tweenA)<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><p>在其他情况下，您可能需要将多个补间链接到另一个补间，以使它们（链接的补间）同时开始动画：</p><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs less"><span class="hljs-selector-tag">tweenA</span><span class="hljs-selector-class">.chain</span>(tweenB,tweenC);<br></code></pre></td></tr></table></figure><blockquote><p>警告：调用 <code>tweenA.chain（tweenB）</code> 实际上修改的是 tweenA，tweenB 总在 tweenA 完成时启动。<code>chain()</code> 的返回值只是 tweenA，不是一个新的 Tween。</p><p>链接多个补间时，比如 <code>tweenA.chain(tweenB, tweenC)</code> 表示 tweenA 动画结束后，tweenB 和 tweenC 动画同时开始，如果因 tweenB 和 tweenC 修改的属性相同，而存在冲突时，经测试写在后面的属性将是最终动画位置。</p><p>注意，一般不要让两个同时开始的补间存在属性冲突。</p></blockquote><ul><li><code>.repeat()</code></li></ul><p>如果想让一个补间永远重复，可以无限链接自己，但更好的方法是使用 <code>repeat()</code> 方法。它接受一个参数，描述第一个补间完成后需要重复多少次，如下代码示例：</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs awk">tween.repeat(<span class="hljs-number">10</span>); <span class="hljs-regexp">//</span> 循环<span class="hljs-number">10</span>次<br>tween.repeat(Infinity); <span class="hljs-regexp">//</span> 无限循环<br></code></pre></td></tr></table></figure><p>我们可以将案例中 <code>simple.html</code> 文件里面的调用改成无限循环，代码如下：</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">tween = <span class="hljs-built_in">new</span> TWEEN.Tween(<span class="hljs-keyword">cube</span>.position).<span class="hljs-keyword">to</span>(&#123;x:<span class="hljs-number">40</span>, y:<span class="hljs-number">30</span>, z:<span class="hljs-number">30</span>&#125;, <span class="hljs-number">2000</span>).repeat(<span class="hljs-keyword">Infinity</span>);<br></code></pre></td></tr></table></figure><ul><li><code>.yoyo()</code></li></ul><p>该方法只有在补间使用 <code>repeat()</code> 方法时才会被调用。我们调用 <code>yoyo()</code> 以后，位置的切换就变成了从头到尾，从尾到头这样的循环过程。</p><p>单个补间无限从头到尾的循环，可以写成这样：</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">tween = <span class="hljs-built_in">new</span> TWEEN.Tween(<span class="hljs-keyword">cube</span>.position).<span class="hljs-keyword">to</span>(&#123;x:<span class="hljs-number">40</span>, y:<span class="hljs-number">30</span>, z:<span class="hljs-number">30</span>&#125;, <span class="hljs-number">2000</span>).repeat(<span class="hljs-keyword">Infinity</span>).yoyo(<span class="hljs-keyword">true</span>);<br></code></pre></td></tr></table></figure><p>进一步了解 <code>yoyo()</code>方法的使用，可查看下面案例。</p><blockquote><p>案例 Demo 查看地址：<a href="https://johnson2heng.github.io/GitChat-Three.js/12第十二节 tween/repeat.html">点击这里</a>。</p><p>案例代码查看地址：<a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/12第十二节 tween/repeat.html">点击这里</a>。</p></blockquote><ul><li><code>.delay()</code></li></ul><p>这个方法用于控制激活前的延时，即触发 <code>start()</code> 事件后，需要延时到设置的 delay 时间，才会真正激活，使用方法见下面代码所示：</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs abnf">tween.delay(<span class="hljs-number">1000</span>)<span class="hljs-comment">;</span><br>tween.start()<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><h3 id="回调函数"><a href="#回调函数" class="headerlink" title="回调函数"></a>回调函数</h3><p>Tween 每次的位置更新后，都会触发 onUpdate 回调函数，我们可以在此回调中修改模型位置。</p><p>在之前案例的 <code>simple.html</code> 中，我们在每次更新回调中获取更新后的位置信息并修改模型几何体的位置：</p><figure class="highlight openscad"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs openscad"><span class="hljs-comment">//设置每次更新的回调，然后修改几何体的位置</span><br>tween.onUpdate(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">(pos)</span> &#123;</span><br>    <span class="hljs-built_in">cube</span>.position.set(pos.x, pos.y, pos.z);<br>&#125;);<br></code></pre></td></tr></table></figure><p>目前，补间支持的回调函数主要有以下几种。</p><ul><li>onStart</li></ul><p>在补间计算开始前的回调，每个补间只能触发一下，即使使用 <code>repeat()</code> 方法循环，这个回调也只被触发一次。</p><ul><li>onStop</li></ul><p>通过调用 <code>stop()</code> 方法停止的补间会触发当前回调，如果是正常完成的补间将不会触发此回调。</p><ul><li>onUpdate</li></ul><p>每次补间更新后，我们可以在此回调中获取更新后的值。</p><ul><li>onComplete</li></ul><p>当补间正常完成时，将会触发此回调。通过使用 <code>stop()</code> 停止的补间将不会触发此回调。</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>在 Three.js 中使用 Tween.js，能够很方便地改变模型的位置，不需要手动计算，便能获取到具体的位置数据，以实现我们的需求。</p><h2 id="Three-js-场景交互"><a href="#Three-js-场景交互" class="headerlink" title="Three.js 场景交互"></a>Three.js 场景交互</h2><p>浏览器是一个 2D 视口，而 Three.js 展示的是 3D 场景。场景交互时，需要在二维平面中控制三维场景的模型，那如何将 2D 视口的 x 和 y 坐标转换成 Three.js 场景中的 3D 坐标呢？</p><p>好在 Three.js 已经有了解决相关问题的方案，那就是 <code>THREE.Raycaster</code> 射线，用于鼠标拾取（计算出鼠标移过的三维空间中的对象）等。我们看下面这张图片：</p><img src="/blog/posts/ab44f639/d5f05ad0-9a20-11e8-8334-9bfa28241acd" class="" title="raycaster"><p>我们一般都会设置三维场景的显示区域，如果指明当前显示的 2D 坐标给 <code>THREE.Raycaster</code>，它将生成一条从显示起点到终点的射线，也就是射线与近视面交点和射线与远视面交点连成的这一条直线。在相机视角下查看，只是一个点，射线会穿过整个显示场景，并按从近到远的顺序返回与模型相交的数据。</p><h3 id="THREE-Raycaster-构造函数和对象方法"><a href="#THREE-Raycaster-构造函数和对象方法" class="headerlink" title="THREE.Raycaster 构造函数和对象方法"></a>THREE.Raycaster 构造函数和对象方法</h3><h4 id="实例化"><a href="#实例化" class="headerlink" title="实例化"></a>实例化</h4><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs reasonml"><span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">Raycaster( <span class="hljs-params">origin</span>, <span class="hljs-params">direction</span>, <span class="hljs-params">near</span>, <span class="hljs-params">far</span> )</span>;<br></code></pre></td></tr></table></figure><p>该实例化函数 Raycaster 包含了四个参数。</p><ul><li>origin：光线投射的原点矢量；</li><li>direction：光线投射的方向矢量，应该是被归一化的；</li><li>near：投射近点，用来限定返回比 near 要远的结果。near 不能为负数，缺省为 0；</li><li>far：投射远点，用来限定返回比 far 要近的结果。far 不能比 near 小，缺省为无穷大。</li></ul><h4 id="属性"><a href="#属性" class="headerlink" title="属性"></a>属性</h4><p><code>THREE.Raycaster</code> 的属性可以在实例化对象后有修改需求时再修改。除了上面提到的 origin、direction、near、far 四个属性外，我们还有可能用到另一个属性：</p><ul><li>linePrecision：射线和线相交的精度，浮点数类型的值。</li></ul><h4 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h4><p><code>THREE.Raycaster</code> 给我们提供了一系列的方法，比如修改射线的位置，判断与某些模型是否相交等，接下来我们列举一些经常使用的方法。</p><ul><li><code>.set()</code>：此方法可以重新设置射线的原点和方向，从而更新射线位置。</li></ul><figure class="highlight sqf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sqf">.<span class="hljs-built_in">set</span>（origin，<span class="hljs-built_in">direction</span>）<br></code></pre></td></tr></table></figure><p>其中，参数 origin 用来设置射线新的原点矢量位置，direction 用来设置基于原点位置的射线的方向矢量。</p><ul><li><code>.setFromCamera()</code>：使用当前相机和界面的 2D 坐标设置射线的位置和方向。</li></ul><figure class="highlight asciidoc"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs asciidoc"><span class="hljs-title">.setFromCamera ( coords, camera )</span><br></code></pre></td></tr></table></figure><p>参数 coords 表示鼠标的二维坐标，在归一化的设备坐标（NDC）中，也就是 X 和 Y 分量，应该介于 -1 和 1 之间。camera 表示射线起点处的相机，即把射线起点设置在该相机位置处。</p><p>点击事件大多通过鼠标触发，我们用鼠标点击显示区域的位置和当前场景使用的相机对象调用此对象，Three.js 会为我们计算出当前射线的位置。</p><ul><li><code>.intersectObject ()</code> 和 <code>.intersectObjects ()</code></li></ul><p>两个方法用来检查射线和物体之间的所有交叉点数据。</p><p>如果检测射线和一个对象是否相交，推荐使用 <code>intersectObject()</code>，如果判断的是这个对象的子对象，那推荐使用 <code>intersectObjects()</code>，将 3D 对象的 children 属性传入。</p><p>返回值是一个交叉点对象数组，且按距离排序，最接近的排在首位。</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">.intersectObject ( <span class="hljs-keyword">object</span>, <span class="hljs-keyword">recursive</span>, optionalTarget)<br></code></pre></td></tr></table></figure><p>参数 object，用来检测和射线相交的物体。如果 recursive 设置为 true，还会向下继续检查所有后代，否则只检查该对象本身，缺省值为 false。optionalTarget 为可选参数，用于设置放置结果的数组，如果缺省，则将会实例化一个新数组，并将获取到的数据放入其中。</p><figure class="highlight angelscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs angelscript">.<span class="hljs-built_in">int</span>ersectObjects ( <span class="hljs-built_in">array</span>, recursive, optionalTarget)<br></code></pre></td></tr></table></figure><p><code>intersectObject()</code> 和 <code>intersectObjects()</code> 的区别在于第一个参数。intersectObject 的第一个参数为 3D 对象，而 intersectObjects 需要传入一个由 3D 对象组成的数组。</p><p><strong>我们知道两个方法的返回值均为对象数组。接下来，我们再进一步了解下这个返回值。</strong></p><p>如果射线与场景内的模型没有相交，将返回一个空数组，否则，将返回一个按从近到远顺序排列的对象数组，数组中每个对象的内容为：</p><figure class="highlight clojure"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs clojure">[ &#123; distance, point, face, faceIndex, indices, object &#125;, ... ]<br></code></pre></td></tr></table></figure><p>其中：</p><ul><li>distance：射线的起点到相交点的距离；</li><li>point：在世界坐标中的交叉点；</li><li>face：相交的面；</li><li>faceIndex：相交的面的索引；</li><li>indices：组成相交面的顶点索引；</li><li>object：相交的对象。</li></ul><p>当一个网孔（Mesh）对象和一个缓存几何模型（BufferGeometry）相交时，faceIndex 将是 undefined，并且 indices 将被设置；而当一个网孔（Mesh）对象和一个几何模型（Geometry）相交时，indices 将是 undefined。</p><p>当计算这个对象是否和射线相交时，Raycaster 把传递的对象委托给检测 3D 对象的 raycast 方法，该方法通过计算，检测出当前模型与射线是否相交。</p><p>这允许 Mesh 对光线投射的反应可以不同于 lines 和 pointclouds。Mesh、lines、pointclouds 是三种不同的计算相交的方法。Mesh 对射线与网格对象的每一个面进行相交判断。lines，则是判断两条线之间的距离，至于 pointclouds，则是通过 distanceSqToPoint 判断当前是否相交。</p><p><strong>注意</strong>，对于网格，面（faces）必须朝向射线原点，这样才能被检测到。背面射线的交叉点将无法被检测到。</p><p>为了使光线能投射到一个对象的正反两面，你需要设置 material 的 side 属性为 THREE.DoubleSide。</p><h3 id="模型点击事件的实现"><a href="#模型点击事件的实现" class="headerlink" title="模型点击事件的实现"></a>模型点击事件的实现</h3><p>上面讲解了射线的相关内容，接下来，我们来看一下，如何使用射线实现一个普通的点击事件。</p><p>首先，我们通过点击事件回调的 event 获取点击的位置：</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-attr">mouse.x</span> = ( event.clientX / window.innerWidth ) * <span class="hljs-number">2</span> - <span class="hljs-number">1</span><span class="hljs-comment">;</span><br><span class="hljs-attr">mouse.y</span> = - ( event.clientY / window.innerHeight ) * <span class="hljs-number">2</span> + <span class="hljs-number">1</span><span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><p>默认没有经过矩阵转换过的显示区域的宽和高分别是 2，即中心点也是 WebGL 场景的坐标原点，左上角的坐标是 <code>(-1.0, 1.0, 0.0)</code>，右下角的坐标是 <code>(1.0, -1.0, 0.0)</code>。我们通过单击点的位置计算出当前该点在场景中，没有被矩阵转换过的平面坐标。如果 WebGL 的渲染区域没有占满窗口，我们还需获取显示区域距离窗口左上角的偏移量，再计算位置，计算方法如下：</p><figure class="highlight mel"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs mel"><span class="hljs-comment">//通过 dom 的 getBoundingClientRect 方法获得当前显示区域距离左上角的偏移量</span><br>var left = <span class="hljs-keyword">renderer</span>.domElement.getBoundingClientRect().left;<br>var top = <span class="hljs-keyword">renderer</span>.domElement.getBoundingClientRect().top;<br><br><span class="hljs-comment">//根据浏览器的设备类型来获取到当前点击的位置</span><br>var clientX = dop.browserRedirect() === <span class="hljs-string">&quot;pc&quot;</span> ? <span class="hljs-keyword">event</span>.clientX - left : <span class="hljs-keyword">event</span>.touches[<span class="hljs-number">0</span>].clientX - left;<br>var clientY = dop.browserRedirect() === <span class="hljs-string">&quot;pc&quot;</span> ? <span class="hljs-keyword">event</span>.clientY - top : <span class="hljs-keyword">event</span>.touches[<span class="hljs-number">0</span>].clientY - top;<br><br><span class="hljs-comment">//计算出场景内的原始坐标</span><br><span class="hljs-keyword">mouse</span>.x = (clientX / <span class="hljs-keyword">renderer</span>.domElement.offsetWidth) * <span class="hljs-number">2</span> - <span class="hljs-number">1</span>;<br><span class="hljs-keyword">mouse</span>.y = -(clientY / <span class="hljs-keyword">renderer</span>.domElement.offsetHeight) * <span class="hljs-number">2</span> + <span class="hljs-number">1</span>;<br></code></pre></td></tr></table></figure><p>获取到坐标以后，我们需要使用射线的 <code>setFromCamera()</code> 方法配合场景坐标和相机更新射线的位置：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">raycaster.set<span class="hljs-constructor">FromCamera( <span class="hljs-params">mouse</span>, <span class="hljs-params">camera</span> )</span>;<br></code></pre></td></tr></table></figure><p>接着，使用 <code>intersectObjects()</code> 方法获取射线和所有模型相交的数组集合：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">var intersects = raycaster.intersect<span class="hljs-constructor">Objects( <span class="hljs-params">scene</span>.<span class="hljs-params">children</span> )</span>;<br></code></pre></td></tr></table></figure><p>这里再提醒一句，很多读者可能发现，有时点击后射线并未获取到相交的物体。这是因为我们一般使用 <code>intersectObject()</code> 和 <code>intersectObjects()</code> 时，只会传入对象，而有的模型由多个模型组成，也就是它的子类，这时我们需要传入第二个值，设置为 true，来提示 Three.js 遍历它的子类。</p><p>最后，如果有与射线相交的模型，返回的 intersects 数组的长度将不为零：</p><figure class="highlight isbl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs isbl"><span class="hljs-function"><span class="hljs-title">if</span>(<span class="hljs-variable">intersects.length</span> &gt; <span class="hljs-number">0</span>)&#123;</span><br><span class="hljs-function">    <span class="hljs-title">alert</span>(<span class="hljs-string">&quot;有相交的模型&quot;</span>);</span><br><span class="hljs-function">&#125;</span><br></code></pre></td></tr></table></figure><p>这里提供一个点击案例，点中物体后，模型颜色将会变色：<a href="https://johnson2heng.github.io/GitChat-Three.js/13第十三节 场景交互/raycaster.html">点击这里</a> 。</p><p>案例源码地址：<a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/13第十三节 场景交互/raycaster.html">点击这里</a>。</p><h3 id="简单框选案例的实现"><a href="#简单框选案例的实现" class="headerlink" title="简单框选案例的实现"></a>简单框选案例的实现</h3><p>最近有些小伙伴想实现一个简单的框选案例，在这一篇中我来带大家完成。</p><p>本案例通过判断模型的位置实现框选，即框选前先获取所有模型在二维平面上的位置，然后再判断这些二维平面上的点是否处于框内。</p><p>相对于其它实现方式，这种实现节约性能，简单易懂，能够应付大部分场景。</p><p>接下来，我讲解一下这个框选的实现思路。</p><p>首先，编写以下代码，当鼠标按下时，记录鼠标按下时的场景坐标：</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs abnf">//获取到显示区域距离窗口左上角的偏移量<br>domClient.x = renderer.domElement.getBoundingClientRect().left<span class="hljs-comment">;</span><br>domClient.y = renderer.domElement.getBoundingClientRect().top<span class="hljs-comment">;</span><br>//计算出当前鼠标距离显示区域左上角的距离<br>down.x = e.clientX - domClient.x<span class="hljs-comment">;</span><br>down.y = e.clientY - domClient.y<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><p>前两行代码求出了当前显示区域距离窗口左上角的偏移量，后两行则计算出来了当前鼠标点击位置距离显示区域左上角的偏移。</p><p>接着，使用 box 对象方法计算出模型的包围盒中心位置，适用于多个复杂模型场景。如果只是简单几何体，可以直接使用 Mesh 的位置来计算。通过相机将世界坐标的位置转换为平面坐标，并将模型放到一个数组内以便后期使用：</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs maxima"><span class="hljs-keyword">for</span> (<span class="hljs-built_in">let</span> i = <span class="hljs-number">0</span>; i &lt; group.children.<span class="hljs-built_in">length</span>; i++) &#123;<br>    <span class="hljs-built_in">let</span> <span class="hljs-built_in">box</span> = <span class="hljs-built_in">new</span> THREE.Box3();<br>    <span class="hljs-built_in">box</span>.expandByObject(group.children[i]);<br><br>    //获取到平面的坐标<br>    <span class="hljs-built_in">let</span> vec3 = <span class="hljs-built_in">new</span> THREE.Vector3();<br>    <span class="hljs-built_in">box</span>.getCenter(vec3);<br>    <span class="hljs-built_in">let</span> vec = vec3.project(camera);<br><br>    modelsList.<span class="hljs-built_in">push</span>(<br>        &#123;<br>            component: group.children[i],<br>            <span class="hljs-built_in">position</span>: &#123;<br>                x: vec.x * half.<span class="hljs-built_in">width</span> + half.<span class="hljs-built_in">width</span>,<br>                y: -vec.y * half.<span class="hljs-built_in">height</span> + half.<span class="hljs-built_in">height</span><br>            &#125;,<br>            normalMaterial: group.children[i].material<br>        &#125;<br>    )<br>&#125;<br></code></pre></td></tr></table></figure><p>上面代码首先通过一个循环，计算出了每一模型在二维平面中的位置。</p><p>接下来，绑定 Document 的 mousemove 事件和 mouseup 事件。鼠标移动事件用来判断每个模型是否处于框内，鼠标抬起事件则将绑定的事件清除。</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs reasonml"><span class="hljs-comment">//绑定鼠标按下移动事件和抬起事件</span><br>document.add<span class="hljs-constructor">EventListener(<span class="hljs-string">&quot;mousemove&quot;</span>, <span class="hljs-params">movefun</span>, <span class="hljs-params">false</span>)</span>;<br>document.add<span class="hljs-constructor">EventListener(<span class="hljs-string">&quot;mouseup&quot;</span>, <span class="hljs-params">upfun</span>, <span class="hljs-params">false</span>)</span>;<br></code></pre></td></tr></table></figure><p>在鼠标移动事件中，我们计算出当前四个边的位置，并且循环判断哪些模型的位置处于框内，处于框内的模型的材质将被修改为框选材质：</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs maxima"><span class="hljs-keyword">for</span> (<span class="hljs-built_in">let</span> i = <span class="hljs-number">0</span>; i &lt; modelsList.<span class="hljs-built_in">length</span>; i++) &#123;<br>    <span class="hljs-built_in">let</span> <span class="hljs-built_in">position</span> = modelsList[i].<span class="hljs-built_in">position</span>;<br>    //判断当前位置是否处于框内<br>    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">position</span>.x &gt; <span class="hljs-built_in">min</span>.x &amp;&amp; <span class="hljs-built_in">position</span>.x &lt; <span class="hljs-built_in">max</span>.x &amp;&amp; <span class="hljs-built_in">position</span>.y &gt; <span class="hljs-built_in">min</span>.y &amp;&amp; <span class="hljs-built_in">position</span>.y &lt; <span class="hljs-built_in">max</span>.y) &#123;<br>        modelsList[i].component.material = material;<br>    &#125;<br>    <span class="hljs-keyword">else</span>&#123;<br>        modelsList[i].component.material = modelsList[i].normalMaterial;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>在最后的鼠标抬起事件内，将框选框隐藏，并将所有材质修改为默认材质：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs reasonml"><span class="hljs-keyword">function</span> upfun(e) &#123;<br><br>    <span class="hljs-comment">//清除事件</span><br>    document.body.remove<span class="hljs-constructor">Child(<span class="hljs-params">div</span>)</span>;<br>    document.remove<span class="hljs-constructor">EventListener(<span class="hljs-string">&quot;mousemove&quot;</span>, <span class="hljs-params">movefun</span>, <span class="hljs-params">false</span>)</span>;<br>    document.remove<span class="hljs-constructor">EventListener(<span class="hljs-string">&quot;mouseup&quot;</span>, <span class="hljs-params">upfun</span>, <span class="hljs-params">false</span>)</span>;<br><br>    <span class="hljs-comment">//将所有的模型修改为当前默认的材质</span><br>    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; modelsList.length; i++) &#123;<br>        modelsList<span class="hljs-literal">[<span class="hljs-identifier">i</span>]</span>.component.material = modelsList<span class="hljs-literal">[<span class="hljs-identifier">i</span>]</span>.normalMaterial;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>最后，附上可以查看的案例地址：<a href="https://johnson2heng.github.io/GitChat-Three.js/13第十三节 场景交互/boxselection.html">点击这里</a>。</p><p>案例源码地址：<a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/13第十三节 场景交互/boxselection.html">点击这里</a></p><h2 id="Three-js-性能优化"><a href="#Three-js-性能优化" class="headerlink" title="Three.js 性能优化"></a>Three.js 性能优化</h2><p>在接触 Three.js 一段时间后，或多或少都会遇到性能问题。此类问题将直接导致页面帧率过低，严重时会导致页面崩溃。</p><p>导致性能问题的原因有很多，比如模型文件太大或顶点数太多导致加载时间过长，甚至失败，又或者代码书写逻辑有问题导致场景帧数太低。面对不同原因，我们需采取不同的应对方案，比如面对模型问题可对模型进行减面、减体积等处理；针对代码问题，则需尽量减少代码的运算。在平时的开发中，我们还要尽可能避免导致这些性能问题的根由。</p><p>下面，我将分享几种简单的有助于提升性能的方法。</p><h3 id="尽量共用几何体和材质"><a href="#尽量共用几何体和材质" class="headerlink" title="尽量共用几何体和材质"></a>尽量共用几何体和材质</h3><p>当大批量进行模型渲染时，不可避免地会有大量重复几何体的创建，这时共用相同的几何体和材质可以减少内存和 GPU 的数据传输。</p><p>我们具体来看一个实例，比如你需要创建三百个简单的相同颜色的立方体模型，普通的实现方法如下：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs reasonml"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">300</span>; i++) &#123;<br>    <span class="hljs-keyword">let</span> geometry = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">BoxGeometry(10, 10, 10)</span>;<br>    <span class="hljs-keyword">let</span> material = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">MeshLambertMaterial(&#123;<span class="hljs-params">color</span>: 0x00ffff&#125;)</span>;<br>    <span class="hljs-keyword">let</span> mesh = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">Mesh(<span class="hljs-params">geometry</span>, <span class="hljs-params">material</span>)</span>;<br>    <span class="hljs-comment">//随机位置</span><br>    mesh.position.set(<span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">THREE</span>.</span><span class="hljs-module"><span class="hljs-identifier">Math</span>.</span></span>rand<span class="hljs-constructor">FloatSpread(200)</span>, <span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">THREE</span>.</span><span class="hljs-module"><span class="hljs-identifier">Math</span>.</span></span>rand<span class="hljs-constructor">FloatSpread(200)</span>, <span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">THREE</span>.</span><span class="hljs-module"><span class="hljs-identifier">Math</span>.</span></span>rand<span class="hljs-constructor">FloatSpread(200)</span>);<br>    group.add(mesh);<br>&#125;<br></code></pre></td></tr></table></figure><p>上面代码创建了三百个相同的几何体和材质，该方法中有很多不必要的创建过程，增加运算的同时，还浪费了内存。</p><p>要解决这些性能问题，创建时我们可以共用相同的几何体和材质，改良后的代码如下所示：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs reasonml"><span class="hljs-keyword">let</span> geometry = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">BoxGeometry(10, 10, 10)</span>;<br><span class="hljs-keyword">let</span> material = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">MeshLambertMaterial(&#123;<span class="hljs-params">color</span>: 0x00ffff&#125;)</span>;<br><span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">300</span>; i++) &#123;<br>    <span class="hljs-keyword">let</span> mesh = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">Mesh(<span class="hljs-params">geometry</span>, <span class="hljs-params">material</span>)</span>;<br>    <span class="hljs-comment">//随机位置</span><br>    mesh.position.set(<span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">THREE</span>.</span><span class="hljs-module"><span class="hljs-identifier">Math</span>.</span></span>rand<span class="hljs-constructor">FloatSpread(200)</span>, <span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">THREE</span>.</span><span class="hljs-module"><span class="hljs-identifier">Math</span>.</span></span>rand<span class="hljs-constructor">FloatSpread(200)</span>, <span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">THREE</span>.</span><span class="hljs-module"><span class="hljs-identifier">Math</span>.</span></span>rand<span class="hljs-constructor">FloatSpread(200)</span>);<br>    group.add(mesh);<br>&#125;<br></code></pre></td></tr></table></figure><p>利用上面代码，我们只需创建一套相同的几何体的顶点数据，不仅降低了内存消耗，还提高了添加运算效率。</p><h3 id="模型删除时，材质和几何体也需从内存中清除"><a href="#模型删除时，材质和几何体也需从内存中清除" class="headerlink" title="模型删除时，材质和几何体也需从内存中清除"></a>模型删除时，材质和几何体也需从内存中清除</h3><p>我们使用 <code>remove()</code> 将模型从场景内删除后，发现内存占用并没有太大变化。这是因为几何体和材质还保存在内存当中，这时需要手动调用 <code>dispose()</code> 方法将其从内存中删除。</p><p>下面为删除整个场景组的案例代码：</p><figure class="highlight xquery"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs xquery">//删除<span class="hljs-keyword">group</span><br><span class="hljs-keyword">function</span> deleteGroup<span class="hljs-built_in">(name</span>) &#123;<br>    <span class="hljs-keyword">let</span> <span class="hljs-keyword">group</span> = scene.getObjectByName<span class="hljs-built_in">(name</span>);<br>    <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">group</span>) <span class="hljs-keyword">return</span>;<br>    //删除掉所有的模型组内的mesh<br>    <span class="hljs-keyword">group</span>.traverse(<span class="hljs-keyword">function</span> (<span class="hljs-type">item</span>) &#123;<br>        <span class="hljs-keyword">if</span> (<span class="hljs-type">item</span> instanceof THREE.Mesh) &#123;<br>            <span class="hljs-type">item</span>.geometry.dispose(); //删除几何体<br>            <span class="hljs-type">item</span>.material.dispose(); //删除材质<br>        &#125;<br>    &#125;);<br><br>    scene<span class="hljs-built_in">.remove</span>(<span class="hljs-keyword">group</span>);<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="使用-merge-方法合并不需要单独操作的模型"><a href="#使用-merge-方法合并不需要单独操作的模型" class="headerlink" title="使用 merge 方法合并不需要单独操作的模型"></a>使用 merge 方法合并不需要单独操作的模型</h3><p>在 Three.js 新版本中，将 merge 方法整合在了几何体上，主要应用于拥有大量几何体且材质相同的模型上。我们可以将多个几何体拼接成单个整体的几何体，从而达到节约性能的目的，但该做法的缺点则是失去了对单个模型的控制。</p><p>通过下面代码，我们了解下 merge 的使用方法：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs reasonml"><span class="hljs-comment">//合并模型，则使用merge方法合并</span><br>var geometry = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">Geometry()</span>;<br><span class="hljs-comment">//merge方法将两个几何体对象或者Object3D里面的几何体对象合并，(使用对象的变换)将几何体的顶点、面、UV 分别合并。</span><br><span class="hljs-comment">//THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead. 如果新版本用老版本的会报这个错</span><br><span class="hljs-keyword">for</span>(var i=<span class="hljs-number">0</span>; i&lt;<span class="hljs-number">20000</span>; i++)&#123;<br>    var cube = add<span class="hljs-constructor">Cube()</span>; <span class="hljs-comment">//创建了一个随机位置的几何体模型</span><br>    cube.update<span class="hljs-constructor">Matrix()</span>; <span class="hljs-comment">//手动更新模型的矩阵</span><br>    geometry.merge(cube.geometry, cube.matrix); <span class="hljs-comment">//将几何体合并</span><br>&#125;<br><br>scene.add(<span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">Mesh(<span class="hljs-params">geometry</span>, <span class="hljs-params">cubeMaterial</span>)</span>);<br></code></pre></td></tr></table></figure><p>我们再看一个案例：<a href="http://www.wjceo.com/blog/threejs/2018-03-14/123.html">点击这里</a>。</p><p>在上面案例中，在不选中 combined 的前提下 ，选择 redraw 20000 个模型的话，一般只有十几帧的帧率。但如果选中了 combined，会发现渲染的帧率可达到满帧（60帧），性能得到了巨大提升。</p><h3 id="在循环渲染中避免使用更新"><a href="#在循环渲染中避免使用更新" class="headerlink" title="在循环渲染中避免使用更新"></a>在循环渲染中避免使用更新</h3><p>几何体、材质、纹理等相关数据的更新尽量不要放到循环渲染中进行。循环渲染时，每一帧都会对 GPU 数据进行更新，这将引发很多不必要的数据更新过程。</p><p>接下来，我们了解下几何体、材质、纹理数据更新的相关属性。</p><ul><li>几何体</li></ul><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-attr">geometry.verticesNeedUpdate</span> = <span class="hljs-literal">true</span><span class="hljs-comment">; //顶点发生了修改</span><br><span class="hljs-attr">geometry.elementsNeedUpdate</span> = <span class="hljs-literal">true</span><span class="hljs-comment">; //面发生了修改</span><br><span class="hljs-attr">geometry.morphTargetsNeedUpdate</span> = <span class="hljs-literal">true</span><span class="hljs-comment">; //变形目标发生了修改</span><br><span class="hljs-attr">geometry.uvsNeedUpdate</span> = <span class="hljs-literal">true</span><span class="hljs-comment">; //uv映射发生了修改</span><br><span class="hljs-attr">geometry.normalsNeedUpdate</span> = <span class="hljs-literal">true</span><span class="hljs-comment">; //法向发生了修改</span><br><span class="hljs-attr">geometry.colorsNeedUpdate</span> = <span class="hljs-literal">true</span><span class="hljs-comment">; //顶点颜色发生的修改</span><br></code></pre></td></tr></table></figure><ul><li>材质</li></ul><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-attr">material.needsUpdate</span> = <span class="hljs-literal">true</span>；<br></code></pre></td></tr></table></figure><ul><li>纹理</li></ul><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-attr">texture.needsUpdate</span> = <span class="hljs-literal">true</span><span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><p>如果它们发生更新，则将其设置为 true，Three.js 会通过判断，将数据重新传输到显存当中，并将配置项重新修改为 false。这是一个十分影响运行效率的过程，我们尽量只在需要的时候修改，且不要放到 <code>render()</code> 方法当中循环设置。</p><h3 id="只在需要的时候渲染"><a href="#只在需要的时候渲染" class="headerlink" title="只在需要的时候渲染"></a>只在需要的时候渲染</h3><p>在没有操作的时候，一直循环渲染属于浪费资源。接下来我带给大家一个只在需要时渲染的方法。</p><p>首先在循环渲染中加入一个判断，如果判断值为 true 时，才可以循环渲染：</p><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs less"><span class="hljs-selector-tag">var</span> <span class="hljs-selector-tag">renderEnabled</span>;<br><span class="hljs-selector-tag">function</span> <span class="hljs-selector-tag">animate</span>() &#123;<br><br>    <span class="hljs-selector-tag">if</span> (renderEnabled) &#123;<br>        <span class="hljs-selector-tag">renderer</span><span class="hljs-selector-class">.render</span>(scene, camera);<br>    &#125;<br><br>    <span class="hljs-selector-tag">requestAnimationFrame</span>(animate);<br>&#125;<br><br><span class="hljs-selector-tag">animate</span>();<br></code></pre></td></tr></table></figure><p>然后设置一个延迟器函数，每次调用后，可以将 renderEnabled 设置为 true，并延迟三秒将其设置为 false，这个延迟时间大家可以根据需求来修改：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">//调用一次可以渲染三秒</span><br><span class="hljs-keyword">let</span> timeOut = <span class="hljs-literal">null</span>;<br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">timeRender</span>(<span class="hljs-params"></span>) </span>&#123;<br>    <span class="hljs-comment">//设置为可渲染状态</span><br>    renderEnabled = <span class="hljs-literal">true</span>;<br>    <span class="hljs-comment">//清除上次的延迟器</span><br>    <span class="hljs-keyword">if</span> (timeOut) &#123;<br>        <span class="hljs-built_in">clearTimeout</span>(timeOut);<br>    &#125;<br><br>    timeOut = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;<br>        renderEnabled = <span class="hljs-literal">false</span>;<br>    &#125;, <span class="hljs-number">3000</span>);<br>&#125;<br></code></pre></td></tr></table></figure><p>接下来，我们在需要的时候调用这个 <code>timeRender()</code> 方法即可，比如在相机控制器更新后的回调中：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">controls.add<span class="hljs-constructor">EventListener(&#x27;<span class="hljs-params">change</span>&#x27;, <span class="hljs-params">function</span>()</span>&#123;<br>    time<span class="hljs-constructor">Render()</span>;<br>&#125;);<br></code></pre></td></tr></table></figure><p>如果相机位置发生变化，就会触发回调，开启循环渲染，更新页面显示。</p><p>如果我们添加了一个模型到场景中，直接调用重新渲染即可：</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs abnf">scene.add(mesh)<span class="hljs-comment">;</span><br>timeRender()<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><p>最后一个重点问题，就是材质的纹理为异步渲染，需要在图片添加完成后，触发回调。好在 Three.js 已经考虑到了这一点，Three.js 的静态对象 <code>THREE.DefaultLoadingManager</code> 的 onLoad 回调会在每一个纹理图片加载完成后触发回调。依靠它，我们可以在 Three.js 的每一个内容发生变更后触发重新渲染，且闲置状态下停止渲染。</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs reasonml"><span class="hljs-comment">//每次材质和纹理更新，触发重新渲染</span><br><span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">THREE</span>.</span><span class="hljs-module"><span class="hljs-identifier">DefaultLoadingManager</span>.</span></span>onLoad = <span class="hljs-keyword">function</span> <span class="hljs-literal">()</span> &#123;<br>    time<span class="hljs-constructor">Render()</span>;<br>&#125;;<br></code></pre></td></tr></table></figure><h2 id="Three-js-核心对象"><a href="#Three-js-核心对象" class="headerlink" title="Three.js 核心对象"></a>Three.js 核心对象</h2><p>本文是介绍 Three.js 核心技术知识的最后一节，带大家了解经常接触到的对象。第16课，也是课程的最后一节，我们就开始实战演练。</p><h3 id="THREE-Clock"><a href="#THREE-Clock" class="headerlink" title="THREE.Clock"></a>THREE.Clock</h3><p>时间对象用于跟踪时间，用于计算每一帧的渲染时间或者从开始渲染到结束的时间。</p><h4 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h4><p>构造函数的使用，如下所示：</p><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs haxe"><span class="hljs-keyword">var</span> clock = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Clock();<br></code></pre></td></tr></table></figure><p>实例化时间对象，还可以接收一个布尔类型的参数，用于设置实例化完成后，是否启动时间对象计时，默认为开启。</p><h4 id="方法-1"><a href="#方法-1" class="headerlink" title="方法"></a>方法</h4><p>该类主要有以下几种方法。</p><ul><li><strong>.start()：</strong>此方法用于启动时间对象开始计时，将所有的信息重置；</li><li><strong>.stop()：</strong>停止时间对象的计时；</li><li><strong>.getElapsedTime()：</strong>此方法返回自时间对象开启以后到现在的时间；</li><li><strong>.getDelta()：</strong>此方法返回上次调用到这次调用此方法的间隔时间，用于计算渲染间隔。</li></ul><h3 id="THREE-Color"><a href="#THREE-Color" class="headerlink" title="THREE.Color"></a>THREE.Color</h3><p>THREE.Color 为颜色类，用到颜色的地方，都可以实例化此类，如设置材质颜色、场景背景颜色等。</p><h4 id="构造函数-1"><a href="#构造函数-1" class="headerlink" title="构造函数"></a>构造函数</h4><p>我们可以通过 <code>new THREE.Color()</code> 传入一个值来初始化一个颜色对象：</p><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs haxe"><span class="hljs-comment">//不传参数将默认渲染成白色</span><br><span class="hljs-keyword">var</span> color = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Color();<br><br><span class="hljs-comment">//十六进制颜色（推荐）</span><br><span class="hljs-keyword">var</span> color = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Color( <span class="hljs-number">0xff0000</span> );<br><br><span class="hljs-comment">//RGB 字符串</span><br><span class="hljs-keyword">var</span> color = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Color(<span class="hljs-string">&quot;rgb(255, 0, 0)&quot;</span>);<br><span class="hljs-keyword">var</span> color = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Color(<span class="hljs-string">&quot;rgb(100%, 0%, 0%)&quot;</span>);<br><br><span class="hljs-comment">//支持所有140个颜色名称</span><br><span class="hljs-keyword">var</span> color = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Color( <span class="hljs-string">&#x27;skyblue&#x27;</span> );<br><br><span class="hljs-comment">//HSL 字符串</span><br><span class="hljs-keyword">var</span> color = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Color(<span class="hljs-string">&quot;hsl(0, 100%, 50%)&quot;</span>);<br><br><span class="hljs-comment">//支持区间为从0到1的RGB数值</span><br><span class="hljs-keyword">var</span> color = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Color( <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span> );<br></code></pre></td></tr></table></figure><h4 id="方法-2"><a href="#方法-2" class="headerlink" title="方法"></a>方法</h4><p>该类主要有以下几种方法。</p><ul><li><strong>.clone()：</strong>返回一个相同颜色的颜色对象；</li><li><strong>.add()：</strong>将一个颜色对象的颜色值和此颜色对象的颜色相加；</li><li><strong>.multiply()：</strong>将一个颜色对象的颜色值和此颜色对象的颜色相乘；</li><li><strong>.set()：</strong>重新设置颜色对象的颜色，支持实例化时的所有方式。</li></ul><h3 id="矢量对象"><a href="#矢量对象" class="headerlink" title="矢量对象"></a>矢量对象</h3><p>矢量是 GLSL ES 语言内的标准数据类型，也是 WebGL 原生着色器语言的数据类型。而 Three.js 为了方便和原生融合，内置了 <code>THREE.Vector2</code>、<code>THREE.Vector3</code> 和 <code>THREE.Vector4</code> 三种矢量，分别代表二维向量、三维向量和四维向量。</p><h4 id="二维矢量"><a href="#二维矢量" class="headerlink" title="二维矢量"></a>二维矢量</h4><p>二维矢量一般表示二维平面上点的位置和方向。</p><p>下面代码生成了一个普通的二维矢量：</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">var</span> a = new THREE.Vector<span class="hljs-number">2</span>( <span class="hljs-number">0</span>, <span class="hljs-number">1</span> );<br></code></pre></td></tr></table></figure><p>我们也可以通过 <code>set()</code> 方法，重新修改二维矢量的值：</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">a</span>.set(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>);<br></code></pre></td></tr></table></figure><h4 id="三维矢量"><a href="#三维矢量" class="headerlink" title="三维矢量"></a>三维矢量</h4><p>在项目当中，我们经常会用到三维矢量，比如模型的位置属性、缩放属性，都是一个三维矢量。它由三个有序的数字组成的。</p><p>使用三维矢量，我们可以表示：</p><ol><li>三维空间中一个点的位置；</li><li>三维空间中两点连线的方向和长度；</li><li>任意有序的三个数字。</li></ol><p>我们看下面这个例子：</p><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs haxe"><span class="hljs-comment">//创建一个普通的三维矢量</span><br><span class="hljs-keyword">var</span> a = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Vector3( <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span> );<br><br><span class="hljs-comment">//默认空值，将会重置为(0, 0, 0)</span><br><span class="hljs-keyword">var</span> b = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Vector3( );<br><br><span class="hljs-comment">//d矢量是从a点到b点的方向和长度</span><br><span class="hljs-keyword">var</span> d = a.distanceTo( b );<br></code></pre></td></tr></table></figure><p>我们经常进行的模型位置改变以及缩放操作，其实都是通过修改三维矢量的值来实现的，示例代码如下：</p><figure class="highlight stata"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs stata"><span class="hljs-comment">//创建一个三维矢量</span><br><span class="hljs-keyword">var</span> <span class="hljs-keyword">vec</span> = new THREE.Vector3(0, 1, 0);<br><br><span class="hljs-comment">//修改模型的位置</span><br>mesh.position = <span class="hljs-keyword">vec</span>;<br><br><span class="hljs-comment">//修改三维矢量的值</span><br><span class="hljs-keyword">vec</span>.<span class="hljs-keyword">set</span>(1, 2, 1);<br><br><span class="hljs-comment">//修改模型的缩放值</span><br>mesh.scale.<span class="hljs-keyword">set</span>(2, 2, 2);<br></code></pre></td></tr></table></figure><p>我们还可以修改三维向量的单个值：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">vec.set<span class="hljs-constructor">Component(0, 20)</span>; <span class="hljs-comment">//将三维向量的第一值修改为20</span><br><br>.set<span class="hljs-constructor">Component(<span class="hljs-params">index</span>, <span class="hljs-params">value</span>)</span>; <br><span class="hljs-comment">//index 修改的单个值的下标，可选值为 0 1 2 对应 vec.x vec.y vec.z</span><br><span class="hljs-comment">//value 修改成的数字</span><br></code></pre></td></tr></table></figure><h4 id="四维矢量"><a href="#四维矢量" class="headerlink" title="四维矢量"></a>四维矢量</h4><p>四维矢量是由四个数字组成的数据类型，四个值分别代表 x、y、z、w。</p><p>四维矢量可以表示的内容有：</p><ol><li>思维空间内的一个点；</li><li>思维空间里的方向和长度；</li><li>任意有序的四个数字。</li></ol><p>我们再看下面这个例子：</p><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs haxe"><span class="hljs-comment">//创建一个普通的四维矢量</span><br><span class="hljs-keyword">var</span> a = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Vector4( <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span> );<br><br><span class="hljs-comment">//如果不设置参数，默认值为 (0, 0, 0, 1)</span><br><span class="hljs-keyword">var</span> b = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Vector4( );<br></code></pre></td></tr></table></figure><h3 id="四维矩阵"><a href="#四维矩阵" class="headerlink" title="四维矩阵"></a>四维矩阵</h3><p>矩阵是高等代数中的常见工具，也常见于统计分析等应用数学学科中。Three.js 为我们封装了三维矩阵（3x3）四维矩阵（4x4）。</p><p>篇幅有限，这里就不介绍三维矩阵了，我们主要看下四维矩阵。</p><p>在 3D 计算机图形中，最常见的四维矩阵主要用于转换矩阵。这代表三维空间中的点 Vector3 通过乘以转换矩阵，可以实现如平移、旋转、剪切、缩放、发射、正交或透视投影等变化。</p><p>在每个 3D 对象中都有三个四维矩阵。</p><ul><li>Object3D.matrix：存储 3D 对象的局部变换，这是 3D 对象相对于父对象的转换；</li><li>Object3D.matrixWorld：3D 对象的全局或世界变换，如果 3D 对象没有父对象，则当前矩阵和其局部变换矩阵相同。</li><li>Object3D.modelViewMatrix：表示 3D 对象相对于摄像机的坐标转换，对象的 modelViewMatrix 是对象的 matrixWorld 再乘以相机的 matrixWorldInverse 获得的结果。</li></ul><p>相机对象不但含有 3D 对象的四维矩阵，还额外拥有以下两个四维矩阵。</p><ul><li>Camera.matrixWorldInverse：视图矩阵（相机的 matrixWorld 反转后的四维矩阵）；</li><li>Camera.projectionMatrix：表示场景内的模型如何转换到二维显示区域的变换矩阵。</li></ul><p>如果你对矩阵的实现原理感兴趣的话，可<a href="https://blog.csdn.net/qq_30100043/article/details/72783071">点击这里</a>查看，这里不再过多解释原理。</p><p>接下来我们查看一下 Three.js 封装的四维变换矩阵为我们提供了哪些方法。</p><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs haxe"><span class="hljs-keyword">var</span> m = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Matrix4();<br><br><span class="hljs-comment">//我们可以通过手动设置每一项的数值来生成变换矩阵</span><br>m.<span class="hljs-keyword">set</span>( <span class="hljs-number">11</span>, <span class="hljs-number">12</span>, <span class="hljs-number">13</span>, <span class="hljs-number">14</span>,<br>       <span class="hljs-number">21</span>, <span class="hljs-number">22</span>, <span class="hljs-number">23</span>, <span class="hljs-number">24</span>,<br>       <span class="hljs-number">31</span>, <span class="hljs-number">32</span>, <span class="hljs-number">33</span>, <span class="hljs-number">34</span>,<br>       <span class="hljs-number">41</span>, <span class="hljs-number">42</span>, <span class="hljs-number">43</span>, <span class="hljs-number">44</span> );<br><br><span class="hljs-comment">//创建一个矢量，并应用此变换矩阵</span><br><span class="hljs-keyword">var</span> v = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Vector3();<br><br>v.applyMatrix4(m); <span class="hljs-comment">//应用变换矩阵</span><br></code></pre></td></tr></table></figure><p>上面代码创建了一个默认的四维矩阵，三维矢量乘以默认的四维矩阵将不会产生变化。</p><p>我们可以使用 <code>.identity()</code> 方法来重置变换矩阵：</p><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs haxe"><span class="hljs-keyword">var</span> m = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Matrix4();<br>m.identity(); <span class="hljs-comment">//重置矩阵恢复默认</span><br></code></pre></td></tr></table></figure><p>我们可以通过 <code>.lookAt()</code> 传入一个三个矢量生成一个旋转矩阵。这三个矢量分别代表眼的位置、查看的物体位置和向上的方向：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">var m = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">Matrix4()</span>;<br>m.look<span class="hljs-constructor">At(<span class="hljs-params">eye</span>, <span class="hljs-params">center</span>, <span class="hljs-params">up</span>)</span>; <span class="hljs-comment">//生成旋转变换矩阵</span><br></code></pre></td></tr></table></figure><p>我们也可以通过旋转弧度来生成旋转变换矩阵：</p><figure class="highlight arcade"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs arcade"><span class="hljs-keyword">var</span> m = <span class="hljs-keyword">new</span> THREE.Matrix4();<br><span class="hljs-comment">//通过绕x轴旋转生成变换矩阵</span><br>m.makeRotationX(Math.<span class="hljs-literal">PI</span>/<span class="hljs-number">2</span>); <span class="hljs-comment">//绕x轴旋转90度变换矩阵</span><br>m.makeRotationAxis(<span class="hljs-keyword">new</span> THREE.Vector3(<span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>), Math.<span class="hljs-literal">PI</span>/<span class="hljs-number">2</span>); <span class="hljs-comment">//效果同上</span><br><br><span class="hljs-comment">//通过绕y轴旋转生成变换矩阵</span><br>m.makeRotationY(Math.<span class="hljs-literal">PI</span>); <span class="hljs-comment">//绕y轴旋转180度变换矩阵</span><br>m.makeRotationAxis(<span class="hljs-keyword">new</span> THREE.Vector3(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>), Math.<span class="hljs-literal">PI</span>); <span class="hljs-comment">//效果同上</span><br><br><span class="hljs-comment">//通过绕z轴旋转生成变换矩阵</span><br>m.makeRotationZ(Math.<span class="hljs-literal">PI</span>/<span class="hljs-number">4</span>); <span class="hljs-comment">//绕z轴旋转45度变换矩阵</span><br>m.makeRotationAxis(<span class="hljs-keyword">new</span> THREE.Vector3(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>), Math.<span class="hljs-literal">PI</span>/<span class="hljs-number">4</span>); <span class="hljs-comment">//效果同上</span><br></code></pre></td></tr></table></figure><p>通过设置缩放来生成缩放变换矩阵：</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">var</span> m = new THREE.Matrix<span class="hljs-number">4</span>();<br><br><span class="hljs-attribute">m</span>.makeScale(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">5</span>); //生成沿x轴不变，y轴放大两倍，z轴放大五倍的缩放变换矩阵<br></code></pre></td></tr></table></figure><p>通过设置位置平移来生成变换矩阵：</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">var</span> m = new THREE.Matrix<span class="hljs-number">4</span>();<br><br><span class="hljs-attribute">m</span>.makeTranslation(<span class="hljs-number">10</span>, -<span class="hljs-number">20</span>, <span class="hljs-number">100</span>); //生成沿x正轴偏移<span class="hljs-number">10</span>，y负轴偏移<span class="hljs-number">20</span>，z正轴偏移<span class="hljs-number">100</span>的平移变换矩阵<br></code></pre></td></tr></table></figure><h3 id="欧拉角"><a href="#欧拉角" class="headerlink" title="欧拉角"></a>欧拉角</h3><p>欧拉角通过在每个轴指定旋转的弧度和指定旋转轴的先后顺序来进行旋转变换。之前介绍场景时已做介绍，不再赘述。这里想说明的是，每个 3D 对象的 rotation 属性都是一个欧拉角对象。</p><p>创建欧拉角定义方法如下：</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">var</span> e = new THREE.Euler( <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>.<span class="hljs-number">57</span>, &#x27;XYZ&#x27; ); <br></code></pre></td></tr></table></figure><p>前三个值分别代表每个轴上旋转的弧度，第四个值是应用旋转的顺序。默认为“XYZ”，这意味着对象将首先围绕其 X 轴旋转，然后围绕其 Y 轴旋转，最后围绕其 Z 轴旋转。其他可能性有 YZX、ZXY、XZY、YXZ、ZYX，都必须大写。</p><p>修改欧拉角的方法如下：</p><figure class="highlight arcade"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs arcade"><span class="hljs-comment">//通过set方法重新设置</span><br><span class="hljs-keyword">var</span> e = <span class="hljs-keyword">new</span> THREE.Euler();<br>e.set(Math.<span class="hljs-literal">PI</span>, <span class="hljs-number">0</span>, - Math.<span class="hljs-literal">PI</span> / <span class="hljs-number">2</span>, <span class="hljs-string">&quot;YZX&quot;</span>); <span class="hljs-comment">//先沿y轴旋转180度，再沿z轴旋转0度，最后沿x轴逆时针旋转90度，第四个值可以不填，默认是&quot;XYZ&quot;</span><br></code></pre></td></tr></table></figure><p>我们也可以通过变换矩阵来修改当前的欧拉角：</p><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs haxe"><span class="hljs-keyword">var</span> e = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Euler( <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1.57</span>, <span class="hljs-string">&#x27;XYZ&#x27;</span> );<br><span class="hljs-keyword">var</span> m = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Matrix4();<br><br>e.setFromRotationMatrix(m); <span class="hljs-comment">//在当前的旋转角度，再进行矩阵变换</span><br></code></pre></td></tr></table></figure><h3 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h3><p>Three.js 知识讲解部分，到这里基本就结束了。第一次写正式的教程，难免有一些瑕疵。</p>]]></content>
    
    
    
    <tags>
      
      <tag>Threejs</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>Threejs学习和总结基础篇</title>
    <link href="/blog/posts/89cc5dea.html"/>
    <url>/blog/posts/89cc5dea.html</url>
    
    <content type="html"><![CDATA[<img src="/blog/posts/89cc5dea/165ea89aa7559037.jpg" class=""><span id="more"></span><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p><strong>使用 Three.js 显示创建的内容，我们必须需要的三大件是：渲染器、相机和场景。</strong>相机获取到场景内显示的内容，然后再通过渲染器渲染到画布上面。</p><p>要在屏幕上展示3D图形，思路大体上都是这样的：</p><ol><li>构建一个三维空间<ul><li>Three中称之为场景(Scene)</li></ul></li><li>选择一个观察点，并确定观察方向/角度等<ul><li>Three中称之为相机(Camera)</li></ul></li><li>在场景中添加供观察的物体<ul><li>Three中的物体有很多种，包括Mesh,Line,Points等，它们都继承自Object3D类</li></ul></li><li>将观察到的场景渲染到屏幕上的指定区域<ul><li>Three中使用Renderer完成这一工作</li></ul></li></ol><p><div class="note info">拿电影来类比的话，场景对应于整个布景空间，相机是拍摄镜头，渲染器用来把拍摄好的场景转换成胶卷。</div></p><h3 id="场景-Scene"><a href="#场景-Scene" class="headerlink" title="场景 Scene"></a>场景 Scene</h3><p>场景是所有物体的容器，也对应着我们创建的三维世界。场景允许你设置哪些对象被Three.js渲染以及渲染在哪里。在场景中放置对象、灯光和相机。</p><h3 id="相机-Camera"><a href="#相机-Camera" class="headerlink" title="相机 Camera"></a>相机 Camera</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// 初始化相机</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initCamera</span>(<span class="hljs-params"></span>) </span>&#123;<br>  camera = <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(<span class="hljs-number">45</span>, <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight, <span class="hljs-number">0.1</span>, <span class="hljs-number">200</span>); <span class="hljs-comment">// 实例化相机</span><br>  camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">15</span>);<br>&#125;<br></code></pre></td></tr></table></figure><h4 id="相机"><a href="#相机" class="headerlink" title="相机"></a>相机</h4><p>Three中的相机有两种，分别是正投影相机THREE.OrthographicCamera和透视投影相机THREE.PerspectiveCamera。 </p><img src="/blog/posts/89cc5dea/v2-3a0604ded6e3edc25e5b12fd63ba97bb_hd-1570630416011.png" class="" title="img"><p>正交投影与透视投影的区别如上图所示，左图是正交投影，物体发出的光平行地投射到屏幕上，远近的方块都是一样大的；右图是透视投影，近大远小，符合我们平时看东西的感觉。<br><a href="https://link.zhihu.com/?target=https%3A//zh.wikipedia.org/wiki/%E4%B8%89%E7%BB%B4%E6%8A%95%E5%BD%B1">维基百科：三维投影</a></p><h5 id="正交投影相机"><a href="#正交投影相机" class="headerlink" title="正交投影相机"></a>正交投影相机</h5><img src="/blog/posts/89cc5dea/v2-62ede52e0bb0d8b49f6cf2e41debc247_hd-1570630416011.jpg" class="" title="img"><p>可以近似地认为，视景体里的物体平行投影到近平面上，然后近平面上的图像被渲染到屏幕上。</p><h5 id="透视投影相机"><a href="#透视投影相机" class="headerlink" title="透视投影相机"></a>透视投影相机</h5><img src="/blog/posts/89cc5dea/v2-3b160a77bda7661c4dd3920ddeaae605_hd-1570630416012.jpg" class="" title="img"><h4 id="坐标系"><a href="#坐标系" class="headerlink" title="坐标系"></a>坐标系</h4><p>Camera是三维世界中的观察者，为了观察这个世界，首先我们要描述空间中的位置，Three中使用采用常见的<a href="https://link.zhihu.com/?target=https%3A//zh.wikipedia.org/wiki/%E7%AC%9B%E5%8D%A1%E5%84%BF%E5%9D%90%E6%A0%87%E7%B3%BB%23.E4.B8.89.E7.B6.AD.E7.A9.BA.E9.96.93">右手坐标系</a>定位：</p><img src="/blog/posts/89cc5dea/zbx-1570630416012.png" class=""><p>我们这里使用到的是 <code>THREE.PerspectiveCamera</code>，这个相机模拟人眼看到的效果，就是具有透视的效果，近大远小。</p><p>第一行，我们实例化了一个透视相机，需要四个值，分别是视野、宽高比、近裁面和远裁面。</p><ul><li>视野：当前相机视野的宽度，值越大，渲染出来的内容也会更多。</li><li>宽高比：默认是按照画布显示的宽高比例来设置，如果比例设置的不对，会发现渲染出来的画面有拉伸或者压缩的感觉。</li><li>近裁面和远裁面：这个是设置相机可以看到的场景内容的范围，如果场景内的内容位置不在这两个值内的话，将不会被显示到渲染的画面中。</li></ul><p>第二行，我们设置了相机的位置。</p><h3 id="渲染器-Renderer"><a href="#渲染器-Renderer" class="headerlink" title="渲染器 Renderer"></a>渲染器 Renderer</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">//初始化渲染器</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initRenderer</span>(<span class="hljs-params"></span>) </span>&#123;<br>    renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer(); <span class="hljs-comment">//实例化渲染器</span><br>    renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight); <span class="hljs-comment">//设置宽和高</span><br>    <span class="hljs-built_in">document</span>.body.appendChild(renderer.domElement); <span class="hljs-comment">//添加到dom</span><br>&#125;<br></code></pre></td></tr></table></figure><p>第一行实例化了一个 <code>THREE.WebGLRenderer</code>，这是一个基于 WebGL 渲染的渲染器，当然，Three.js 向下兼容，还有 CanvasRenderer、CSS2DRenderer、CSS3DRenderer 和 SVGRenderer，这四个渲染器分别基于 canvas2D、CSS2D、CSS3D 和 SVG 渲染的渲染器。由于，作为 3D 渲染，WebGL 渲染的效果最好，并且支持的功能更多。</p><p>第二行，调用了一个设置函数 setSize 方法，这个是设置需要显示的窗口大小。案例是基于浏览器全屏显示，所以设置了浏览器窗口的宽和高。</p><p>第三行，<code>renderer.domElement</code> 是在实例化渲染器时生成的一个 Canvas 画布，渲染器渲染界面生成的内容，都将在这个画布上显示。所以，我们将这个画布添加到了 DOM 当中，来显示渲染的内容。</p><h3 id="模型-Object3D"><a href="#模型-Object3D" class="headerlink" title="模型 Object3D"></a>模型 Object3D</h3><p>渲染器，场景和相机都全了，是不是就能显示东西了？不能！因为场景内没有内容，即使渲染出来也是一片漆黑，所以我们需要往场景里面添加内容。接下来，我们将查看 initMesh 方法，看看如何创建一个最简单的模型：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">//创建模型</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initMesh</span>(<span class="hljs-params"></span>) </span>&#123;<br>    geometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry( <span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span> ); <span class="hljs-comment">//创建几何体</span><br>    material = <span class="hljs-keyword">new</span> THREE.MeshNormalMaterial(); <span class="hljs-comment">//创建材质</span><br><br>    mesh = <span class="hljs-keyword">new</span> THREE.Mesh( geometry, material ); <span class="hljs-comment">//创建网格</span><br>    scene.add( mesh ); <span class="hljs-comment">//将网格添加到场景</span><br>&#125;<br></code></pre></td></tr></table></figure><p>创建一个网格（模型）需要两种对象：几何体和材质。</p><ul><li>几何体代表模型的形状，它是由固定的点的位置组成，点绘制出面，面组成了模型。</li><li>材质是我们看到当前模型显示出来的效果，如显示的颜色，质感等。</li></ul><p>Three中供显示的物体有很多，它们都继承自Object3D类</p><h4 id="Mesh"><a href="#Mesh" class="headerlink" title="Mesh"></a>Mesh</h4><p>我们都知道，计算机的世界里，一条弧线是由有限个点构成的有限条线段连接得到的。线段很多时，看起来就是一条平滑的弧线了。<br>计算机中的三维模型也是类似的，普遍的做法是用三角形组成的网格来描述，我们把这种模型称之为Mesh模型。 </p><img src="/blog/posts/89cc5dea/v2-d1ac417178674e359837b5795edca3b3_hd-1570630416012.jpg" class="" title="img"><blockquote><p>这是那只著名的<a href="https://link.zhihu.com/?target=https%3A//en.wikipedia.org/wiki/Stanford_bunny">斯坦福兔子</a>。它在3D图形中的地位与数字图像处理领域中著名的<a href="[https://baike.baidu.com/item/%E8%8E%B1%E5%A8%9C%E5%9B%BE/3797874?fr=aladdin">Lenna</a>是类似的。<br>看这只兔子，随着三角形数量的增加，它的表面越来越平滑</p></blockquote><p>在Three中，Mesh的构造函数是这样的：Mesh( geometry, material )<br>geometry是它的形状，material是它的材质。<br>不止是Mesh，创建很多物体都要用到这两个属性。下面我们来看看这两个重要的属性。Material和Geometry是相辅相成的，必须结合使用。</p><h5 id="Geometry"><a href="#Geometry" class="headerlink" title="Geometry"></a>Geometry</h5><p>Geometry，形状，相当直观。Geometry通过存储模型用到的点集和点间关系(哪些点构成一个三角形)来达到描述物体形状的目的。<br>Three提供了立方体(其实是长方体)、平面(其实是长方形)、球体、圆形、圆柱、圆台等许多基本形状；<br>你也可以通过自己定义每个点的位置来构造形状；<br>对于比较复杂的形状，我们还可以通过外部的模型文件导入。</p><h5 id="Material"><a href="#Material" class="headerlink" title="Material"></a>Material</h5><p>Material，材质，这就没有形状那么直观了。<br>材质其实是物体表面除了形状以为所有可视属性的集合，例如色彩、纹理、光滑度、透明度、反射率、折射率、发光度。<br>这里讲一下材质(Material)、贴图(Map)和纹理(Texture)的关系。<br>材质上面已经提到了，它包括了贴图以及其它。<br>贴图其实是‘贴’和‘图’，它包括了图片和图片应当贴到什么位置。<br>纹理嘛，其实就是‘图’了。<br>Three提供了多种材质可供选择，能够自由地选择漫反射/镜面反射等材质。</p><h3 id="光影Light"><a href="#光影Light" class="headerlink" title="光影Light"></a>光影Light</h3><p>神说：要有光！<br>光影效果是让画面丰富的重要因素。<br>Three提供了包括环境光AmbientLight、点光源PointLight、 聚光灯SpotLight、方向光DirectionalLight、半球光HemisphereLight等多种光源。<br>只要在场景中添加需要的光源就好了。</p><h3 id="让场景动起来"><a href="#让场景动起来" class="headerlink" title="让场景动起来"></a>让场景动起来</h3><p>动画，就是多幅图片一直切换便可显示动画的效果。为了能显示动画的效果，我们首先要了解一个函数 requestAnimationFrame，这个函数专门为了动画而出现。它与 setInterval 相比，优势在于不需要设置多长时间重新渲染，而是在当前线程内 JS 空闲时自动渲染，并且最大帧数控制在一秒60帧。所以，我们书写了一个可以循环调用的函数：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>&#123;<br>    requestAnimationFrame(animate); <span class="hljs-comment">//循环调用函数</span><br>    <span class="hljs-comment">// ...</span><br>&#125;<br></code></pre></td></tr></table></figure><p>在循环调用的函数中，每一帧我们都让页面重新渲染相机拍摄下来的内容：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">renderer.render( scene, camera ); <span class="hljs-comment">//渲染界面</span><br></code></pre></td></tr></table></figure><p>渲染的 render 方法需要两个值，第一个值是场景对象，第二个值是相机对象。这意味着，你可以有多个相机和多个场景，可以通过渲染不同的场景和相机让画布上显示不同的画面。</p><p>但是，如果现在一直渲染的话，我们发现就一个立方体在那，也没有动，我们需要做的是让立方体动起来：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs js">mesh.rotation.x += <span class="hljs-number">0.01</span>; <span class="hljs-comment">//每帧网格模型的沿x轴旋转0.01弧度</span><br>mesh.rotation.y += <span class="hljs-number">0.02</span>; <span class="hljs-comment">//每帧网格模型的沿y轴旋转0.02弧度</span><br></code></pre></td></tr></table></figure><p>每一个实例化的网格对象都有一个 rotation 的值，通过设置这个值可以让立方体旋转起来。在每一帧里，我们让立方体沿 x 轴方向旋转0.01弧度，沿 y 轴旋转0.02弧度（1π 弧度等于180度角度）。</p><h3 id="Threejs性能检测插件"><a href="#Threejs性能检测插件" class="headerlink" title="Threejs性能检测插件"></a>Threejs性能检测插件</h3><p>在 Three.js 里面，遇到最多的问题就是性能问题，所以我们需要时刻检测当前的 Three.js 的性能。现在 Three.js 常使用的一款插件叫 stats。接下来我们看看如何将 stats 插件在 Three.js 的项目中使用。</p><ul><li>首先在页面中引入插件代码：</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">&lt;script src=<span class="hljs-string">&quot;http://www.wjceo.com/lib/js/libs/stats.min.js&quot;</span>&gt;&lt;/script&gt;<br></code></pre></td></tr></table></figure><p>这是     一个 CDN 的地址，直接引入即可。</p><ul><li>然后，我们需要实例化一个 stats 对象，然后把对象内生成的 DOM 添加到页面当中。</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs js">stats = <span class="hljs-keyword">new</span> Stats();<br><span class="hljs-built_in">document</span>.body.appendChild(stats.dom);<br></code></pre></td></tr></table></figure><ul><li>最后一步，我们需要在 requestAnimationFrame 的回调里面更新每次渲染的时间：</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>&#123;<br>    requestAnimationFrame(animate); <span class="hljs-comment">//循环调用函数</span><br>    stats.update(); <span class="hljs-comment">//更新性能插件</span><br>    renderer.render( scene, camera ); <span class="hljs-comment">//渲染界面</span><br>&#125;<br></code></pre></td></tr></table></figure><p>使用了性能检测插件以后，整个代码如下：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">&quot;utf-8&quot;</span> /&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span></span><br><span class="hljs-tag">      <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;viewport&quot;</span></span><br><span class="hljs-tag">      <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0&quot;</span></span><br><span class="hljs-tag">    /&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">&quot;X-UA-Compatible&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;ie=edge&quot;</span> /&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Stats插件案例<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css"></span><br><span class="css">      <span class="hljs-selector-tag">body</span> &#123;</span><br><span class="css">        <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;</span><br><span class="css">      &#125;</span><br><span class="css"></span><br><span class="css">      <span class="hljs-selector-tag">canvas</span> &#123;</span><br><span class="css">        <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;</span><br><span class="css">        <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;</span><br><span class="css">        <span class="hljs-attribute">display</span>: block;</span><br><span class="css">      &#125;</span><br><span class="css">    </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">onload</span>=<span class="hljs-string">&quot;init()&quot;</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;https://cdn.bootcss.com/three.js/92/three.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;http://www.wjceo.com/lib/js/libs/stats.min.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript"></span><br><span class="javascript">      <span class="hljs-comment">//声明一些全局变量</span></span><br><span class="javascript">      <span class="hljs-keyword">var</span> renderer, camera, scene, geometry, material, mesh, stats;</span><br><span class="javascript">      <span class="hljs-comment">//初始化渲染器</span></span><br><span class="javascript">      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initRenderer</span>(<span class="hljs-params"></span>) </span>&#123;</span><br><span class="javascript">        renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer(); <span class="hljs-comment">//实例化渲染器</span></span><br><span class="javascript">        renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight); <span class="hljs-comment">//设置宽和高</span></span><br><span class="javascript">        <span class="hljs-built_in">document</span>.body.appendChild(renderer.domElement); <span class="hljs-comment">//添加到dom</span></span><br><span class="javascript">      &#125;</span><br><span class="javascript"></span><br><span class="javascript">      <span class="hljs-comment">//初始化场景</span></span><br><span class="javascript">      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initScene</span>(<span class="hljs-params"></span>) </span>&#123;</span><br><span class="javascript">        scene = <span class="hljs-keyword">new</span> THREE.Scene(); <span class="hljs-comment">//实例化场景</span></span><br><span class="javascript">      &#125;</span><br><span class="javascript"></span><br><span class="javascript">      <span class="hljs-comment">//初始化相机</span></span><br><span class="javascript">      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initCamera</span>(<span class="hljs-params"></span>) </span>&#123;</span><br><span class="javascript">        camera = <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(</span><br><span class="javascript">          <span class="hljs-number">45</span>,</span><br><span class="javascript">          <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight,</span><br><span class="javascript">          <span class="hljs-number">0.1</span>,</span><br><span class="javascript">          <span class="hljs-number">200</span></span><br><span class="javascript">        ); <span class="hljs-comment">//实例化相机</span></span><br><span class="javascript">        camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">15</span>);</span><br><span class="javascript">      &#125;</span><br><span class="javascript"></span><br><span class="javascript">      <span class="hljs-comment">//创建模型</span></span><br><span class="javascript">      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initMesh</span>(<span class="hljs-params"></span>) </span>&#123;</span><br><span class="javascript">        geometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>); <span class="hljs-comment">//创建几何体</span></span><br><span class="javascript">        material = <span class="hljs-keyword">new</span> THREE.MeshNormalMaterial(); <span class="hljs-comment">//创建材质</span></span><br><span class="javascript">        mesh = <span class="hljs-keyword">new</span> THREE.Mesh(geometry, material); <span class="hljs-comment">//创建网格</span></span><br><span class="javascript">        scene.add(mesh); <span class="hljs-comment">//将网格添加到场景</span></span><br><span class="javascript">      &#125;</span><br><span class="javascript"></span><br><span class="javascript">      <span class="hljs-comment">//运行动画</span></span><br><span class="javascript">      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>&#123;</span><br><span class="javascript">        requestAnimationFrame(animate); <span class="hljs-comment">//循环调用函数</span></span><br><span class="javascript">        mesh.rotation.x += <span class="hljs-number">0.01</span>; <span class="hljs-comment">//每帧网格模型的沿x轴旋转0.01弧度</span></span><br><span class="javascript">        mesh.rotation.y += <span class="hljs-number">0.02</span>; <span class="hljs-comment">//每帧网格模型的沿y轴旋转0.02弧度</span></span><br><span class="javascript">        stats.update(); <span class="hljs-comment">//更新性能检测框</span></span><br><span class="javascript">        renderer.render(scene, camera); <span class="hljs-comment">//渲染界面</span></span><br><span class="javascript">      &#125;</span><br><span class="javascript"></span><br><span class="javascript">      <span class="hljs-comment">//性能检测框</span></span><br><span class="javascript">      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initStats</span>(<span class="hljs-params"></span>) </span>&#123;</span><br><span class="javascript">        stats = <span class="hljs-keyword">new</span> Stats();</span><br><span class="javascript">        <span class="hljs-built_in">document</span>.body.appendChild(stats.dom);</span><br><span class="javascript">      &#125;</span><br><span class="javascript"></span><br><span class="javascript">      <span class="hljs-comment">//初始化函数，页面加载完成是调用</span></span><br><span class="javascript">      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">init</span>(<span class="hljs-params"></span>) </span>&#123;</span><br><span class="javascript">        initRenderer();</span><br><span class="javascript">        initScene();</span><br><span class="javascript">        initCamera();</span><br><span class="javascript">        initMesh();</span><br><span class="javascript">        initStats();</span><br><span class="javascript">        animate();</span><br><span class="javascript">      &#125;</span><br><span class="javascript">    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span><br></code></pre></td></tr></table></figure><h2 id="场景Scene"><a href="#场景Scene" class="headerlink" title="场景Scene"></a>场景Scene</h2><p>场景是我们每个 Three.js 项目里面放置内容的容器，我们也可以拥有多个场景进行切换展示，你可以在场景内放置你的模型、灯光和照相机。还可以通过调整场景的位置，让场景内的所有内容都一起跟着调整位置。</p><img src="/blog/posts/89cc5dea/scence.png" class=""><h3 id="场景的结构"><a href="#场景的结构" class="headerlink" title="场景的结构"></a>场景的结构</h3><p>之前在刚刚开始学 JavaScript 基础时，我们总免不了去操作 DOM 对象，而且我们都知道 DOM 的结构是树形结构的，Three.js 也遵循了这样的理念，将所有可以添加到场景内的结构梳理成了一种树形的结构，方便我们能够更好的理解Three.js。</p><p>我们可以把 Scene 想象成一个 body，body 内可以添加 DOM 对象，scene 内也可以添加它的 3D 对象，这样一层层的嵌套出来，组成了我们现在需要的项目。所以，在 Three.js 中，为了方便操作，将所有 3D 对象共同的内容抽象成了一个基类，就是 <code>THREE.Object3D</code>。</p><h3 id="THREE-Object3D"><a href="#THREE-Object3D" class="headerlink" title="THREE.Object3D"></a>THREE.Object3D</h3><p>为了方便操作，Three.js 将每个能够直接添加到场景内的对象都继承自一个基类——<code>THREE.Object3D</code>，以后我们将继承自这个基类的对象称为 3D 对象，判断一个对象是否是继承自 <code>THREE.Object3D</code>，我们可以这么做：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs js">obj <span class="hljs-keyword">instanceof</span> THREE.Object3D<br><span class="hljs-comment">//继承至返回 true 否则返回false</span><br></code></pre></td></tr></table></figure><p>这个基类上封装了我们常用的一些方法，下面我们分别介绍下。</p><h4 id="添加一个-3D-对象"><a href="#添加一个-3D-对象" class="headerlink" title="添加一个 3D 对象"></a>添加一个 3D 对象</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">scene.add(mesh); <span class="hljs-comment">//将网格添加到场景</span><br></code></pre></td></tr></table></figure><p>将一个立方体添加到场景内显示。</p><p>这个方法不光能够在场景内使用，而且也可以将一个 3D 对象添加到另一个 3D 对象里面，代码如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">parent.add(child);<br></code></pre></td></tr></table></figure><h4 id="获取一个-3D-对象"><a href="#获取一个-3D-对象" class="headerlink" title="获取一个 3D 对象"></a>获取一个 3D 对象</h4><p>获取一个 3D 对象可以使用 getObjectByName 通过 3D 对象的 name 值进行获取，在获取前我们首先要设置当前 3D 对象的 name 值：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js">object3D.name = <span class="hljs-string">&quot;firstObj&quot;</span>;<br>scene.add(object3D);<br>scene.getObjectByName(<span class="hljs-string">&quot;firstObj&quot;</span>); <span class="hljs-comment">//返回第一个匹配的3d对象</span><br></code></pre></td></tr></table></figure><p>另一种方式就是使用 getObjectById 通过 3D 对象的 id 值进行获取，3D 对象的 id 值只能读取，它是在添加到场景时，按 1、2、3、4、5……的顺序默认生成的一个值，无法自定义：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">scene.getObjectById(<span class="hljs-number">1</span>); <span class="hljs-comment">//返回id值为1的3d对象</span><br></code></pre></td></tr></table></figure><h4 id="删除一个-3D-对象"><a href="#删除一个-3D-对象" class="headerlink" title="删除一个 3D 对象"></a>删除一个 3D 对象</h4><p>如果我们想隐藏一个 3D 对象，而不让它显示，可以通过设置它的 visible的值来实现：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">mesh.visible = <span class="hljs-literal">false</span>; <span class="hljs-comment">//设置为false，模型将不会被渲染到场景内</span><br></code></pre></td></tr></table></figure><p>如果一个模型不再被使用到，需要彻底删除，我们可以使用 remove 方法进行删除：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs js">scene.add(mesh); <span class="hljs-comment">//将一个模型添加到场景当中</span><br>scene.remove(mesh); <span class="hljs-comment">//将一个模型从场景中删除</span><br></code></pre></td></tr></table></figure><h4 id="获取到所有的子类"><a href="#获取到所有的子类" class="headerlink" title="获取到所有的子类"></a>获取到所有的子类</h4><p>每一个 3D 对象都有一个 children 属性，这是一个数组，里面包含所有添加的 3D 对象：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs js">scene.add(mesh1);<br>scene.add(mesh2);<br><span class="hljs-built_in">console</span>.log(scene.children);<br><span class="hljs-comment">// [mesh1, mesh2]</span><br></code></pre></td></tr></table></figure><p>如果想获取 3D 对象下面所有的 3D 对象，我们可以通过 traverse方法获取：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs js">mesh1.add(mesh2); <span class="hljs-comment">//mesh2是mesh1的子元素</span><br>scene.add(mesh1); <span class="hljs-comment">//mesh1是场景对象的子元素</span><br>scene.traverse(<span class="hljs-function"><span class="hljs-title">fucntion</span>(<span class="hljs-params">child</span>)</span>&#123;<br>    <span class="hljs-built_in">console</span>.log(child);<br>&#125;);<br><span class="hljs-comment">//将按顺序分别将mesh1和mesh2打印出来</span><br></code></pre></td></tr></table></figure><h4 id="获取-3D-对象的父元素"><a href="#获取-3D-对象的父元素" class="headerlink" title="获取 3D 对象的父元素"></a>获取 3D 对象的父元素</h4><p>每个 3D 对象都有一个父元素，可以通过 parent 属性进行获取：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs js">scene.add(mesh); <span class="hljs-comment">//将模型添加到场景</span><br><span class="hljs-built_in">console</span>.log(mesh.parent === scene); <span class="hljs-comment">//true</span><br></code></pre></td></tr></table></figure><h4 id="修改-3D-对象"><a href="#修改-3D-对象" class="headerlink" title="修改 3D 对象"></a>修改 3D 对象</h4><p>前面介绍了场景的结构以及场景的 3D 对象添删查，下面，我们接着介绍对场景内模型的一些操作。</p><h5 id="修改位置方式"><a href="#修改位置方式" class="headerlink" title="修改位置方式"></a>修改位置方式</h5><p>我们可以通过设置模型的 position 属性来修改模型的当前位置，具体方法有以下几种。</p><ul><li>单独设置每个方向的属性。</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js">mesh.position.x = <span class="hljs-number">3</span>; <span class="hljs-comment">//将模型的位置调整到x正轴距离原点为3的位置。</span><br>mesh.position.y += <span class="hljs-number">5</span>; <span class="hljs-comment">//将模型的y轴位置以当前的位置向上移动5个单位。</span><br>mesh.position.z -= <span class="hljs-number">6</span>;<br></code></pre></td></tr></table></figure><ul><li>直接一次性设置所有方向的属性。</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">mesh.position.set(<span class="hljs-number">3</span>, <span class="hljs-number">5</span>, -<span class="hljs-number">6</span>);  <span class="hljs-comment">//直接将模型的位置设置在x轴为3，y轴为5，z轴为-6的位置</span><br></code></pre></td></tr></table></figure><ul><li>Three.js 模型的位置属性是一个 <code>THREE.Vector3</code>（三维向量）的对象，我们可以直接重新赋值一个新的对象。</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">mesh.position = <span class="hljs-keyword">new</span> THREE.Vector3(<span class="hljs-number">3</span>, <span class="hljs-number">5</span>, -<span class="hljs-number">6</span>); <span class="hljs-comment">//上面的设置位置也可以通过这样设置。</span><br></code></pre></td></tr></table></figure><h5 id="修改大小的方式"><a href="#修改大小的方式" class="headerlink" title="修改大小的方式"></a>修改大小的方式</h5><p>模型导入后，很多情况下都需要调整模型的大小。我们可以通过设置模型的 scale 属性来调整大小。</p><ul><li>第一种方式是单独设置每个方向的缩放。</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js">mesh.scale.x = <span class="hljs-number">2</span>; <span class="hljs-comment">//模型沿x轴放大一倍</span><br>mesh.scale.y = <span class="hljs-number">0.5</span>; <span class="hljs-comment">//模型沿y轴缩小一倍</span><br>mesh.scale.z = <span class="hljs-number">1</span>; <span class="hljs-comment">//模型沿z轴保持不变</span><br></code></pre></td></tr></table></figure><ul><li>第二种是使用 set 方法。</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs js">mesh.scale.set(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>); <span class="hljs-comment">//每个方向等比放大一倍</span><br>mesh.scale.set(<span class="hljs-number">0.5</span>, <span class="hljs-number">0.5</span>, <span class="hljs-number">0.5</span>); <span class="hljs-comment">//每个方向等比缩小一倍</span><br></code></pre></td></tr></table></figure><ul><li>第三种方式，由于 scale 属性也是一个三维向量，我们可以通过赋值的方式重新修改。</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">mesh.scale = <span class="hljs-keyword">new</span> THREE.Vector3(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>); <span class="hljs-comment">//每个方向都放大一倍</span><br></code></pre></td></tr></table></figure><h5 id="修改模型的转向"><a href="#修改模型的转向" class="headerlink" title="修改模型的转向"></a>修改模型的转向</h5><p>很多情况下，我们需要对模型进行旋转，以达到将模型显示出它需要显示的方位，我们可以通过设置模型的 rotation 属性进行旋转（注意：旋转 Three.js 使用的是弧度不是角度）。</p><ul><li>第一种方式是单独设置每个轴的旋转。</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js">mesh.rotation.x = <span class="hljs-built_in">Math</span>.PI; <span class="hljs-comment">//模型沿x旋转180度</span><br>mesh.rotation.y = <span class="hljs-built_in">Math</span>.PI * <span class="hljs-number">2</span>; <span class="hljs-comment">//模型沿y轴旋转360度，跟没旋转一样的效果。。。</span><br>mesh.rotation.z = - <span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">2</span>; <span class="hljs-comment">//模型沿z轴逆时针旋转90度 </span><br></code></pre></td></tr></table></figure><ul><li>第二种方式就是使用 set 方法重新赋值。</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">mesh.rotation.set(<span class="hljs-built_in">Math</span>.PI, <span class="hljs-number">0</span>, - <span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">2</span>); <span class="hljs-comment">//旋转效果和第一种显示的效果相同</span><br></code></pre></td></tr></table></figure><p>正常模型的旋转方式是按照 XYZ 依次旋转的，如果你想先旋转其他轴，我们可以添加第四项修改，有可能的情况为：YZX、ZXY、XZY、YXZ 和 ZYX。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">mesh.rotation.set(<span class="hljs-built_in">Math</span>.PI, <span class="hljs-number">0</span>, - <span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">2</span>, <span class="hljs-string">&quot;YZX&quot;</span>); <span class="hljs-comment">//先沿y轴旋转180度，再沿z轴旋转0度，最后沿x轴逆时针旋转90度</span><br></code></pre></td></tr></table></figure><ul><li>第三种方式，模型的 rotation 属性其实是一个欧拉角对象（<code>THREE.Euler</code>），欧拉角后面会讲解到，我们可以通过重新赋值一个欧拉角对象来实现旋转调整：</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">mesh.rotation = <span class="hljs-keyword">new</span> THREE.Euler(<span class="hljs-built_in">Math</span>.PI, <span class="hljs-number">0</span>, - <span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">2</span>, <span class="hljs-string">&quot;YZX&quot;</span>); <br></code></pre></td></tr></table></figure><h3 id="使用-dat-GUI-实现页面调试"><a href="#使用-dat-GUI-实现页面调试" class="headerlink" title="使用 dat.GUI 实现页面调试"></a>使用 dat.GUI 实现页面调试</h3><p>有些时候，我们需要调整模型的位置或者大小等，且需要每次都去场景内调试，一种常用的插件 <code>dat.GUI</code></p><ul><li>首先，需要将插件的源码引入到页面当中，我这里直接使用 CDN 的连接。</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">&lt;script src=<span class="hljs-string">&quot;https://cdn.bootcss.com/dat-gui/0.7.1/dat.gui.min.js&quot;</span>&gt;&lt;/script&gt;<br></code></pre></td></tr></table></figure><ul><li>创建一个对象，在里面设置我们需要修改的一些数据。</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js">controls = &#123;<br>    <span class="hljs-attr">positionX</span>:<span class="hljs-number">0</span>,<br>    <span class="hljs-attr">positionY</span>:<span class="hljs-number">0</span>,<br>    <span class="hljs-attr">positionZ</span>:<span class="hljs-number">0</span><br>&#125;;<br></code></pre></td></tr></table></figure><ul><li>实例化<code>dat.GUI</code>对象，将需要修改的配置添加对象中，并监听变化回调。</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs js">gui = <span class="hljs-keyword">new</span> dat.GUI();<br>gui.add(controls, <span class="hljs-string">&quot;positionX&quot;</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">1</span>).onChange(updatePosition);<br>gui.add(controls, <span class="hljs-string">&quot;positionY&quot;</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">1</span>).onChange(updatePosition);<br>gui.add(controls, <span class="hljs-string">&quot;positionZ&quot;</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">1</span>).onChange(updatePosition);<br><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updatePosition</span>(<span class="hljs-params"></span>) </span>&#123;<br>    mesh.position.set(controls.positionX, controls.positionY, controls.positionZ);<br>&#125;<br></code></pre></td></tr></table></figure><img src="/blog/posts/89cc5dea/11b4aae0-60ff-11e8-8a60-1bdde4cc4659" class="" title="enter image description here"><p>这样，只要我们每次修改对象里面的值，都会触发 <code>updatePosition</code> 回调，来更新模型的位置。这样，我们就实现了一个简单的案例。</p><p>接下来，我列出一下经常会使用到一些方式和方法。</p><h4 id="生成一个输入框"><a href="#生成一个输入框" class="headerlink" title="生成一个输入框"></a>生成一个输入框</h4><p><code>dat.GUI</code> 能够根据 controls 值的不同而生成不同的操作方法，如果值的类型为字符串或者数字类型，则可以生成一个默认的输入框：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js">gui.add(controls, <span class="hljs-string">&quot;positionX&quot;</span>);<br>gui.add(controls, <span class="hljs-string">&quot;positionY&quot;</span>);<br>gui.add(controls, <span class="hljs-string">&quot;positionZ&quot;</span>);<br></code></pre></td></tr></table></figure><img src="/blog/posts/89cc5dea/ff916b00-60fe-11e8-b977-f33e31f528f0" class="" title="enter image description here"><h4 id="生成一个可以滑动的滑块"><a href="#生成一个可以滑动的滑块" class="headerlink" title="生成一个可以滑动的滑块"></a>生成一个可以滑动的滑块</h4><p>使用 <code>gui.add()</code> 方法，如果值为数字类型，传入的第三个值（最小值）和第四个值（最大值），就限制了值能够取值的范围，这样就生成了可以滑动的滑块：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js">gui.add(controls, <span class="hljs-string">&quot;positionX&quot;</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">1</span>); <span class="hljs-comment">//设置了最小值和最大值，可以生成滑块</span><br>gui.add(controls, <span class="hljs-string">&quot;positionY&quot;</span>).max(<span class="hljs-number">1</span>); <span class="hljs-comment">//只设置了最大值，无法生成滑块</span><br>gui.add(controls, <span class="hljs-string">&quot;positionZ&quot;</span>).min(-<span class="hljs-number">1</span>); <span class="hljs-comment">//只设置了最小值，也无法生成滑块</span><br></code></pre></td></tr></table></figure><img src="/blog/posts/89cc5dea/d87954b0-60fe-11e8-a59f-c7ac04233ce1" class="" title="enter image description here"><p>我们还可以通过 step() 方法来限制每次变化的最小值，也就是你增加或者减少，必须都是这个值的倍数：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js">gui.add(controls, <span class="hljs-string">&quot;positionX&quot;</span>, -<span class="hljs-number">10</span>, <span class="hljs-number">10</span>).step(<span class="hljs-number">1</span>); <span class="hljs-comment">//限制必须为整数</span><br>gui.add(controls, <span class="hljs-string">&quot;positionY&quot;</span>, -<span class="hljs-number">10</span>, <span class="hljs-number">10</span>).step(<span class="hljs-number">0.1</span>); <span class="hljs-comment">//每次变化都是0.1的倍数</span><br>gui.add(controls, <span class="hljs-string">&quot;positionZ&quot;</span>, -<span class="hljs-number">10</span>, <span class="hljs-number">10</span>).step(<span class="hljs-number">10</span>); <span class="hljs-comment">//每次变化都是10的倍数</span><br></code></pre></td></tr></table></figure><img src="/blog/posts/89cc5dea/360d2c00-60ff-11e8-b977-f33e31f528f0" class="" title="step"><h4 id="生成一个下拉框"><a href="#生成一个下拉框" class="headerlink" title="生成一个下拉框"></a>生成一个下拉框</h4><p>只要按规则在 <code>gui.add()</code> 的第三个值传入一个对象或者数组，<code>dat.GUI</code> 就能够自动匹配生成一个下拉框：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs js">controls = &#123;<br>    <span class="hljs-attr">positionX</span>:<span class="hljs-number">0</span>,<br>    <span class="hljs-attr">positionY</span>:<span class="hljs-literal">false</span>,<br>    <span class="hljs-attr">positionZ</span>:<span class="hljs-string">&quot;middle&quot;</span><br>&#125;;<br><br>gui = <span class="hljs-keyword">new</span> dat.GUI();<br>gui.add(controls, <span class="hljs-string">&quot;positionX&quot;</span>, &#123;<span class="hljs-attr">left</span>:-<span class="hljs-number">10</span>, <span class="hljs-attr">middle</span>:<span class="hljs-number">0</span>, <span class="hljs-attr">right</span>:<span class="hljs-number">10</span>&#125;); <span class="hljs-comment">//数字类型的下拉框</span><br>gui.add(controls, <span class="hljs-string">&quot;positionY&quot;</span>, [<span class="hljs-literal">true</span>, <span class="hljs-literal">false</span>]); <span class="hljs-comment">//布尔值类型的下拉框</span><br>gui.add(controls, <span class="hljs-string">&quot;positionZ&quot;</span>, [<span class="hljs-string">&quot;left&quot;</span>, <span class="hljs-string">&quot;middle&quot;</span>, <span class="hljs-string">&quot;right&quot;</span>]); <span class="hljs-comment">//字符串类型的下拉框</span><br></code></pre></td></tr></table></figure><p><img src="http://images.gitbook.cn/00b45540-6101-11e8-b864-0bd1f4b74dfb" alt="下拉框"></p><h4 id="生成一个-Checkbox"><a href="#生成一个-Checkbox" class="headerlink" title="生成一个 Checkbox"></a>生成一个 Checkbox</h4><p>只要 controls 的值是一个布尔类型，使用 <code>gui.add()</code> 方法就可以生成一个复选框：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs js">controls = &#123;<br>    <span class="hljs-attr">positionX</span>:<span class="hljs-literal">true</span>,<br>    <span class="hljs-attr">positionY</span>:<span class="hljs-literal">false</span>,<br>    <span class="hljs-attr">positionZ</span>:<span class="hljs-literal">false</span><br>&#125;;<br><br>gui = <span class="hljs-keyword">new</span> dat.GUI();<br>gui.add(controls, <span class="hljs-string">&quot;positionX&quot;</span>);<br>gui.add(controls, <span class="hljs-string">&quot;positionY&quot;</span>);<br>gui.add(controls, <span class="hljs-string">&quot;positionZ&quot;</span>);<br></code></pre></td></tr></table></figure><img src="/blog/posts/89cc5dea/68ceb940-6101-11e8-8a60-1bdde4cc4659" class="" title="复选框"><h4 id="生成一个点击事件按钮"><a href="#生成一个点击事件按钮" class="headerlink" title="生成一个点击事件按钮"></a>生成一个点击事件按钮</h4><p>如果 controls 的值为一个函数 Function，<code>dat.GUI</code> 会自动生成一个可以点击的按钮，当按下时就会触发这个函数事件：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs js">controls = &#123;<br>    <span class="hljs-attr">positionX</span>:<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;&#125;,<br>    <span class="hljs-attr">positionY</span>:<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;&#125;,<br>    <span class="hljs-attr">positionZ</span>:<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;&#125;<br>&#125;;<br><br>gui = <span class="hljs-keyword">new</span> dat.GUI();<br>gui.add(controls, <span class="hljs-string">&quot;positionX&quot;</span>);<br>gui.add(controls, <span class="hljs-string">&quot;positionY&quot;</span>);<br>gui.add(controls, <span class="hljs-string">&quot;positionZ&quot;</span>);<br></code></pre></td></tr></table></figure><img src="/blog/posts/89cc5dea/d30f6330-6102-11e8-8a60-1bdde4cc4659" class="" title="事件按钮"><h4 id="修改显示的名称"><a href="#修改显示的名称" class="headerlink" title="修改显示的名称"></a>修改显示的名称</h4><p>我们可以在后面使用 name() 事件设置显示的名称：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js">gui.add(controls, <span class="hljs-string">&quot;positionX&quot;</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">1</span>).name(<span class="hljs-string">&quot;x轴&quot;</span>);<br>gui.add(controls, <span class="hljs-string">&quot;positionY&quot;</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">1</span>).name(<span class="hljs-string">&quot;y轴&quot;</span>);<br>gui.add(controls, <span class="hljs-string">&quot;positionZ&quot;</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">1</span>).name(<span class="hljs-string">&quot;z轴&quot;</span>);<br></code></pre></td></tr></table></figure><img src="/blog/posts/89cc5dea/bb928ce0-6103-11e8-b977-f33e31f528f0" class="" title="自定义显示名称"><h4 id="颜色选择框"><a href="#颜色选择框" class="headerlink" title="颜色选择框"></a>颜色选择框</h4><p>实现颜色选择框，首先需要一种正常格式的颜色值，比如 CSS 的颜色样式或者 RGB 格式，然后再使用 <code>gui.addColor()</code> 的方法添加：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs js">controls = &#123;<br>    <span class="hljs-attr">positionX</span>:<span class="hljs-string">&quot;#cccccc&quot;</span>, <span class="hljs-comment">//css样式</span><br>    <span class="hljs-attr">positionY</span>: [<span class="hljs-number">0</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>], <span class="hljs-comment">//RGB格式</span><br>    <span class="hljs-attr">positionZ</span>: [<span class="hljs-number">0</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">0.6</span>] <span class="hljs-comment">//RGBA格式</span><br>&#125;;<br><br>gui = <span class="hljs-keyword">new</span> dat.GUI();<br>gui.addColor(controls, <span class="hljs-string">&quot;positionX&quot;</span>).name(<span class="hljs-string">&quot;x轴&quot;</span>);<br>gui.addColor(controls, <span class="hljs-string">&quot;positionY&quot;</span>).name(<span class="hljs-string">&quot;y轴&quot;</span>);<br>gui.addColor(controls, <span class="hljs-string">&quot;positionZ&quot;</span>).name(<span class="hljs-string">&quot;z轴&quot;</span>);<br></code></pre></td></tr></table></figure><img src="/blog/posts/89cc5dea/e6796900-6104-11e8-8a60-1bdde4cc4659" class="" title="颜色选择框"><h4 id="监听事件回调"><a href="#监听事件回调" class="headerlink" title="监听事件回调"></a>监听事件回调</h4><p><code>dat.GUI</code> 给我们提供了监听事件回调的方法 onChange()，如果值变化就能够触发函数回调：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs js">gui.add(controls, <span class="hljs-string">&quot;positionX&quot;</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">1</span>).onChange(updatePosition);<br>gui.add(controls, <span class="hljs-string">&quot;positionY&quot;</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">1</span>).onChange(updatePosition);<br>gui.add(controls, <span class="hljs-string">&quot;positionZ&quot;</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">1</span>).onChange(updatePosition);<br><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updatePosition</span>(<span class="hljs-params"></span>) </span>&#123;<br>    mesh.position.set(controls.positionX, controls.positionY, controls.positionZ);<br>&#125;<br></code></pre></td></tr></table></figure><h4 id="创建分组"><a href="#创建分组" class="headerlink" title="创建分组"></a>创建分组</h4><p>我们可以使用 <code>gui.addFolder()</code> 方法来创建分组：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs js">gui = <span class="hljs-keyword">new</span> dat.GUI();<br><span class="hljs-keyword">var</span> first = gui.addFolder(<span class="hljs-string">&quot;第一个分组&quot;</span>); <span class="hljs-comment">//创建第一个分组</span><br>first.add(controls, <span class="hljs-string">&quot;positionX&quot;</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">1</span>).onChange(updatePosition);<br>first.open();<br><br><span class="hljs-keyword">var</span> two = gui.addFolder(<span class="hljs-string">&quot;第二个分组&quot;</span>);<br>two.add(controls, <span class="hljs-string">&quot;positionY&quot;</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">1</span>).onChange(updatePosition);<br>two.add(controls, <span class="hljs-string">&quot;positionZ&quot;</span>, -<span class="hljs-number">1</span>, <span class="hljs-number">1</span>).onChange(updatePosition);<br></code></pre></td></tr></table></figure><img src="/blog/posts/89cc5dea/3d02f730-6107-11e8-8a60-1bdde4cc4659" class="" title="分组"><h2 id="几何体Geometry"><a href="#几何体Geometry" class="headerlink" title="几何体Geometry"></a>几何体Geometry</h2><p>一个模型是由几何体 Geometry 和材质 Material 组成。Three.js 内置了很多的几何体种类，如立方体、三棱锥、球、八面体、十二面体、二十面体等等，将介绍这些几何体的模型创建和几何体的通用方法。</p><img src="/blog/posts/89cc5dea/geometry-1570631173098.png" class=""><h3 id="Geometry-和-BufferGeometry"><a href="#Geometry-和-BufferGeometry" class="headerlink" title="Geometry 和 BufferGeometry"></a>Geometry 和 BufferGeometry</h3><p>当前 Three.js 内置了这两种几何体类型，这两个几何体类型都用于存储模型的顶点位置、面的索引、法向量、颜色、UV 纹理以及一些自定义的属性。</p><p>它们两个的区别是：BufferGeometry 存储的都是一些原始的数据，性能比 Geometry 高，很适合存储一些放入场景内不需要再额外操作的模型。而 Geometry 的优势刚好相反，Geometry 比 BufferGeometry 更友好，使用了 Three.js 提供的 <code>THREE.Vector3</code> 或者 <code>THREE.Color</code> 这样的对象来存储数据（顶点位置、面、颜色等），这些对象易于阅读和编辑，但效率低于 BufferGeometry 使用的类型化数组。</p><p>所以，我们可以根据项目的大小来使用不同的几何体，<strong>小项目可以使用 Geometry 实现，中大型的项目还是推荐 BufferGeometry。</strong></p><p>我们将使用较为简单的 Geometry 来实现案例。</p><h4 id="Geometry-和-BufferGeometry-互转"><a href="#Geometry-和-BufferGeometry-互转" class="headerlink" title="Geometry 和 BufferGeometry 互转"></a>Geometry 和 BufferGeometry 互转</h4><p>在这里插一点内容，两种几何体类型可以互转，所以，不要担心现在使用的是哪种。</p><ul><li>Geometry 转换成 BufferGeometry</li></ul><p>转换代码，如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">//实例化一个Geometry对象</span><br><span class="hljs-keyword">var</span> geo = <span class="hljs-keyword">new</span> THREE.Geometry(); <br><span class="hljs-comment">//调用对象的fromBufferGeometry方法，并将需要转换的geometry传入</span><br><span class="hljs-keyword">var</span> bufferGeo = geo.fromBufferGeometry(geometry);<br><span class="hljs-comment">//返回的对象转换成的BufferGeometry</span><br></code></pre></td></tr></table></figure><ul><li><code>BufferGeometry</code>转换成<code>Geometry</code></li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">//实例化一个BufferGeometry对象</span><br><span class="hljs-keyword">var</span> bufferGeo = <span class="hljs-keyword">new</span> THREE.BufferGeometry(); <br><span class="hljs-comment">//调用对象的fromGeometry方法，并将需要转换的bufferGeometry传入</span><br><span class="hljs-keyword">var</span> geo = bufferGeo.fromGeometry(bufferGeometry);<br><span class="hljs-comment">//返回的对象转换成的Geometry</span><br></code></pre></td></tr></table></figure><p>接下来，我们将讲解一下 Three.js 内置几何体。</p><h4 id="立方体-BoxGeometry-和-BoxBufferGeometry"><a href="#立方体-BoxGeometry-和-BoxBufferGeometry" class="headerlink" title="立方体 BoxGeometry 和 BoxBufferGeometry"></a>立方体 BoxGeometry 和 BoxBufferGeometry</h4><p>立方体是最早接触的几何体，可以通过设置长宽高来创建各种各样的立方体。</p><p>看下面的案例，代码：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">var</span> geometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry( <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span> );<br><span class="hljs-keyword">var</span> material = <span class="hljs-keyword">new</span> THREE.MeshBasicMaterial( &#123;<span class="hljs-attr">color</span>: <span class="hljs-number">0x00ff00</span>&#125; );<br><span class="hljs-keyword">var</span> cube = <span class="hljs-keyword">new</span> THREE.Mesh( geometry, material );<br>scene.add( cube );<br></code></pre></td></tr></table></figure><p>案例中的构造函数：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">BoxGeometry(width : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">height</span> : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">depth</span> : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">widthSegments</span> : <span class="hljs-string">&#x27;整数类型&#x27;</span>, <span class="hljs-attr">heightSegments</span> : <span class="hljs-string">&#x27;整数类型&#x27;</span>, <span class="hljs-attr">depthSegments</span> : <span class="hljs-string">&#x27;整数类型&#x27;</span>)<br></code></pre></td></tr></table></figure><p>各参数的含义：</p><ul><li>width：沿 X 轴的宽度，默认值为1；</li><li>height：沿 Y 轴的高度，默认值为1；</li><li>depth：沿 Z 轴的深度，默认值为1；</li><li>widthSegments：可选，沿着边的宽度的分割面的数量。默认值为1；</li><li>heightSegments：可选，沿着边的高度的分割面的数量。默认值为1；</li><li>depthSegments：可选，沿着边的深度的分割面的数量。缺省值是1；</li></ul><p>案例查看：<a href="https://threejs.org/docs/scenes/geometry-browser.html#BoxGeometry">请点击这里</a>。</p><h4 id="圆-CircleGeometry-和-CircleBufferGeometry"><a href="#圆-CircleGeometry-和-CircleBufferGeometry" class="headerlink" title="圆 CircleGeometry 和 CircleBufferGeometry"></a>圆 CircleGeometry 和 CircleBufferGeometry</h4><p><strong>在 WebGL 里，所有的模型都是通过三角形面组成，圆形是由多个三角形分段构成，这些三角形分段围绕一个中心点延伸并且延伸到给定半径以外。它从起始角度和给定的中心角度逆时针方向构建。它也可以用来创建规则的多边形，其中线段的数量决定了边的数量。</strong></p><p>看下面案例，代码如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">var</span> geometry = <span class="hljs-keyword">new</span> THREE.CircleGeometry( <span class="hljs-number">5</span>, <span class="hljs-number">32</span> );<br><span class="hljs-keyword">var</span> material = <span class="hljs-keyword">new</span> THREE.MeshBasicMaterial( &#123; <span class="hljs-attr">color</span>: <span class="hljs-number">0xffff00</span> &#125; );<br><span class="hljs-keyword">var</span> circle = <span class="hljs-keyword">new</span> THREE.Mesh( geometry, material );<br>scene.add( circle );<br></code></pre></td></tr></table></figure><p>案例中的构造函数，如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">CircleGeometry(radius : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">segments</span> : <span class="hljs-string">&#x27;整数类型&#x27;</span>, <span class="hljs-attr">thetaStart</span> : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">thetaLength</span> : <span class="hljs-string">&#x27;浮点类型&#x27;</span>)<br></code></pre></td></tr></table></figure><p>各参数的含义：</p><ul><li>radius：圆的半径，默认值为1；</li><li>segments：段数（三角形），最小值为3，默认值为8；</li><li>thetaStart：第一段的起始角度，默认值为0；</li><li>thetaLength：圆形扇形的中心角，通常称为 theta。默认值是 2 * Pi，画出一个整圆。</li></ul><p>案例查看：<a href="https://threejs.org/docs/scenes/geometry-browser.html#CircleGeometry">请点击这里</a>。</p><h4 id="圆锥-ConeGeometry-和-ConeBufferGeometry"><a href="#圆锥-ConeGeometry-和-ConeBufferGeometry" class="headerlink" title="圆锥 ConeGeometry 和 ConeBufferGeometry"></a>圆锥 ConeGeometry 和 ConeBufferGeometry</h4><p>这是一个可以创建圆锥体的类。</p><p>看下面案例，代码如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">var</span> geometry = <span class="hljs-keyword">new</span> THREE.ConeGeometry( <span class="hljs-number">5</span>, <span class="hljs-number">20</span>, <span class="hljs-number">32</span> );<br><span class="hljs-keyword">var</span> material = <span class="hljs-keyword">new</span> THREE.MeshBasicMaterial( &#123;<span class="hljs-attr">color</span>: <span class="hljs-number">0xffff00</span>&#125; );<br><span class="hljs-keyword">var</span> cone = <span class="hljs-keyword">new</span> THREE.Mesh( geometry, material );<br>scene.add( cone );<br></code></pre></td></tr></table></figure><p>案例中的构造函数，如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">ConeGeometry(radius : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">height</span> : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">radialSegments</span> : <span class="hljs-string">&#x27;整数类型&#x27;</span>, <span class="hljs-attr">heightSegments</span> : <span class="hljs-string">&#x27;整数类型&#x27;</span>, <span class="hljs-attr">openEnded</span> : <span class="hljs-string">&#x27;布尔类型&#x27;</span>, <span class="hljs-attr">thetaStart</span> : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">thetaLength</span> : <span class="hljs-string">&#x27;浮点类型&#x27;</span>)<br></code></pre></td></tr></table></figure><p>各参数含义：</p><ul><li>radius：底部圆锥的半径，默认值为1；</li><li>height：圆锥体的高度，默认值为1；</li><li>radialSegments：圆锥周围的分段面数，默认值为8；</li><li>heightSegments：沿圆锥体高度的面的行数，默认值为1；</li><li>openEnded：圆锥体底部是是隐藏还是显示，默认值为 false，显示；</li><li>thetaStart：第一段的起始角度，默认值是0（Three.js 的0度位置）</li><li>thetaLength — 圆形扇形的中心角，通常称为 theta。默认值是2 * Pi，画出一个整圆。</li></ul><p>案例查看：<a href="https://threejs.org/docs/scenes/geometry-browser.html#ConeGeometry">请点击这里</a>。</p><h4 id="圆柱-CylinderGeometry-和-CylinderBufferGeometry"><a href="#圆柱-CylinderGeometry-和-CylinderBufferGeometry" class="headerlink" title="圆柱 CylinderGeometry 和 CylinderBufferGeometry"></a>圆柱 CylinderGeometry 和 CylinderBufferGeometry</h4><p>这是一个可以创建圆柱几何体的类。</p><p>看下面案例，代码如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">var</span> geometry = <span class="hljs-keyword">new</span> THREE.CylinderGeometry( <span class="hljs-number">5</span>, <span class="hljs-number">5</span>, <span class="hljs-number">20</span>, <span class="hljs-number">32</span> );<br><span class="hljs-keyword">var</span> material = <span class="hljs-keyword">new</span> THREE.MeshBasicMaterial( &#123;<span class="hljs-attr">color</span>: <span class="hljs-number">0xffff00</span>&#125; );<br><span class="hljs-keyword">var</span> cylinder = <span class="hljs-keyword">new</span> THREE.Mesh( geometry, material );<br>scene.add( cylinder );<br></code></pre></td></tr></table></figure><p>案例中的构造函数，如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">CylinderGeometry(radiusTop : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">radiusBottom</span> : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">height</span> : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">radialSegments</span> : <span class="hljs-string">&#x27;整数类型&#x27;</span>, <span class="hljs-attr">heightSegments</span> : <span class="hljs-string">&#x27;整数类型&#x27;</span>, <span class="hljs-attr">openEnded</span> : <span class="hljs-string">&#x27;布尔类型&#x27;</span>, <span class="hljs-attr">thetaStart</span> : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">thetaLength</span> : <span class="hljs-string">&#x27;浮点类型&#x27;</span>)<br></code></pre></td></tr></table></figure><p>各参数含义：</p><ul><li>radiusTop：顶部圆柱体的半径。默认值为1；</li><li>radiusBottom：底部圆柱体的半径。默认值为1；</li><li>height：圆柱体的高度。默认值为1；</li><li>radialSegments：圆柱周围的分段面数。默认值为8；</li><li>heightSegments：沿圆柱体高度的面的行数。默认值为1；</li><li>openEnded：圆柱体的两端是否显示，默认值是 false，显示；</li><li>thetaStart：第一段的起始角度，默认值是0（Three.js 的0度位置）。</li><li>thetaLength — 圆形扇形的中心角，通常称为 theta。默认值是2 * Pi，画出一个整圆。</li></ul><p>案例查看：<a href="https://threejs.org/docs/scenes/geometry-browser.html#CylinderGeometry">请点击这里</a>。</p><h4 id="球-SphereGeometry-和-SphereBufferGeometry"><a href="#球-SphereGeometry-和-SphereBufferGeometry" class="headerlink" title="球 SphereGeometry 和 SphereBufferGeometry"></a>球 SphereGeometry 和 SphereBufferGeometry</h4><p>这是一个可以创建球体几何体的类。</p><p>看下面案例，代码如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">var</span> geometry = <span class="hljs-keyword">new</span> THREE.SphereGeometry( <span class="hljs-number">5</span>, <span class="hljs-number">32</span>, <span class="hljs-number">32</span> );<br><span class="hljs-keyword">var</span> material = <span class="hljs-keyword">new</span> THREE.MeshBasicMaterial( &#123;<span class="hljs-attr">color</span>: <span class="hljs-number">0xffff00</span>&#125; );<br><span class="hljs-keyword">var</span> sphere = <span class="hljs-keyword">new</span> THREE.Mesh( geometry, material );<br>scene.add( sphere );<br></code></pre></td></tr></table></figure><p>案例中的构造函数，如下 ：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">SphereGeometry(radius : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">widthSegments</span> : <span class="hljs-string">&#x27;整数类型&#x27;</span>, <span class="hljs-attr">heightSegments</span> : <span class="hljs-string">&#x27;整数类型&#x27;</span>, <span class="hljs-attr">phiStart</span> : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">phiLength</span> : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">thetaStart</span> : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">thetaLength</span> : <span class="hljs-string">&#x27;浮点类型&#x27;</span>)<br></code></pre></td></tr></table></figure><p>各参数含义：</p><ul><li>radius：球体半径。默认值是1；</li><li>widthSegments：水平线段的数量。最小值是3，默认值是8；</li><li>heightSegments：垂直段的数量。最小值是2，默认值是6；</li><li>phiStart：指定水平渲染起始角度。默认值为0；</li><li>phiLength：指定水平渲染角度大小。默认值是 Math.PI * 2；</li><li>thetaStart：指定垂直渲染起始角度。默认值为0；</li><li>thetaLength：指定垂直渲染角度大小。默认是 Math.PI。</li></ul><p>默认是渲染整个的圆，如果线段越多，显得球体越圆滑。</p><p>案例查看：<a href="https://threejs.org/docs/scenes/geometry-browser.html#SphereGeometry">请点击这里</a>。</p><h4 id="平面-PlaneGeometry-和-SphereBufferGeometry"><a href="#平面-PlaneGeometry-和-SphereBufferGeometry" class="headerlink" title="平面 PlaneGeometry 和 SphereBufferGeometry"></a>平面 PlaneGeometry 和 SphereBufferGeometry</h4><p>这是一个可以创建平面几何体的类。</p><p>看下面案例，代码如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">var</span> geometry = <span class="hljs-keyword">new</span> THREE.PlaneGeometry( <span class="hljs-number">5</span>, <span class="hljs-number">20</span>, <span class="hljs-number">32</span> );<br><span class="hljs-keyword">var</span> material = <span class="hljs-keyword">new</span> THREE.MeshBasicMaterial( &#123;<span class="hljs-attr">color</span>: <span class="hljs-number">0xffff00</span>, <span class="hljs-attr">side</span>: THREE.DoubleSide&#125; );<br><span class="hljs-keyword">var</span> plane = <span class="hljs-keyword">new</span> THREE.Mesh( geometry, material );<br>scene.add( plane );<br></code></pre></td></tr></table></figure><p>案例中的构造函数，如下 ：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">PlaneGeometry(width : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">height</span> : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">widthSegments</span> : <span class="hljs-string">&#x27;整数类型&#x27;</span>, <span class="hljs-attr">heightSegments</span> : <span class="hljs-string">&#x27;整数类型&#x27;</span>)<br></code></pre></td></tr></table></figure><p>各参数含义：</p><ul><li>width：沿 X 轴的宽度。默认值为1；</li><li>height：沿着 Y 轴的高度。默认值为1；</li><li>widthSegments：宽度的分段数，可选。默认值为1；</li><li>heightSegments：高度的分段数，可选。默认值为1。</li></ul><p>案例查看：<a href="https://threejs.org/docs/scenes/geometry-browser.html#PlaneGeometry">请点击这里</a>。</p><h4 id="圆环-TorusGeometry-和-TorusBufferGeometry"><a href="#圆环-TorusGeometry-和-TorusBufferGeometry" class="headerlink" title="圆环 TorusGeometry 和 TorusBufferGeometry"></a>圆环 TorusGeometry 和 TorusBufferGeometry</h4><p>一个可以创建圆环几何体的类。</p><p>看下面案例，代码如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">var</span> geometry = <span class="hljs-keyword">new</span> THREE.TorusGeometry( <span class="hljs-number">10</span>, <span class="hljs-number">3</span>, <span class="hljs-number">16</span>, <span class="hljs-number">100</span> );<br><span class="hljs-keyword">var</span> material = <span class="hljs-keyword">new</span> THREE.MeshBasicMaterial( &#123; <span class="hljs-attr">color</span>: <span class="hljs-number">0xffff00</span> &#125; );<br><span class="hljs-keyword">var</span> torus = <span class="hljs-keyword">new</span> THREE.Mesh( geometry, material );<br>scene.add( torus );<br></code></pre></td></tr></table></figure><p>案例中的构造函数，如下 ：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">TorusGeometry(radius : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">tube</span> : <span class="hljs-string">&#x27;浮点类型&#x27;</span>, <span class="hljs-attr">radialSegments</span> : <span class="hljs-string">&#x27;整数类型&#x27;</span>, <span class="hljs-attr">tubularSegments</span> : <span class="hljs-string">&#x27;整数类型&#x27;</span>, <span class="hljs-attr">arc</span> : <span class="hljs-string">&#x27;浮点类型&#x27;</span>)<br></code></pre></td></tr></table></figure><p>各参数含义：</p><ul><li>radius：圆环的半径，从圆环的中心到管的中心。默认值为1；</li><li>tube：管的半径。默认值是0.4；</li><li>radialSegments：横向分段数，默认值是8；</li><li>tubularSegments：纵向分段数，默认值是6；</li><li>arc — 绘制的弧度。默认值是 Math.PI * 2，绘制整个圆环。</li></ul><p>案例查看：<a href="https://threejs.org/docs/scenes/geometry-browser.html#TorusGeometry">请点击这里</a>。</p><p>以上是 Three.js 内置的一些基础的几何体，Three.js 还内置了一些其他的几何体模型（如字体几何体、拉伸几何体、车床几何体等），由于篇幅原因和难度原因，在这里不一一讲解。</p><p>如果迫不及待得想了解这一块的内容可以通过<a href="https://threejs.org/docs/">官方文档</a>或者我的博客中<a href="https://blog.csdn.net/qq_30100043/article/category/7003591">关于 Three.js 的笔记</a>来了解更多。</p><h3 id="常用方法"><a href="#常用方法" class="headerlink" title="常用方法"></a>常用方法</h3><p>Geometry 和 BufferGeomety 内置了一些常用的方法，在每一种几何体上面，我们都可以调用相关的方法来达到我们的目的。</p><h4 id="center"><a href="#center" class="headerlink" title="center()"></a>center()</h4><p>此方法为居中方法，可以根据边界框居中几何图形。</p><h4 id="computeBoundingBox"><a href="#computeBoundingBox" class="headerlink" title="computeBoundingBox()"></a>computeBoundingBox()</h4><p>此方法可以计算几何的边界框，方法调用后，会更新 <code>Geometry.boundingBox</code> 属性，我们可以通过 <code>Geometry.boundingBox</code> 属性获取到一个包围几何体的立方体的每个轴向的最大值和最小值。</p><h4 id="dispose"><a href="#dispose" class="headerlink" title="dispose()"></a>dispose()</h4><p>将几何体从内存中删除，这个方法必须记得使用。如果频繁的删除模型，一定要记得将几何体从内存中删除掉。</p><h3 id="总结案例"><a href="#总结案例" class="headerlink" title="总结案例"></a>总结案例</h3><p>最后，我将上面介绍的所有几何体放到一个场景内，制作了一个小案例，案例代码地址如下：</p><ul><li><a href="https://github.com/Cenergy/webpack-threejs/blob/master/src/test/second.js">Github 地址</a></li></ul><h2 id="材质Material"><a href="#材质Material" class="headerlink" title="材质Material"></a>材质Material</h2><p>模型的表现，也就是我们看到的的模型外观——材质。</p><img src="/blog/posts/89cc5dea/materials.png" class=""><p>简单的说，就是物体看起来是什么质地。材质可以看成是材料和质感的结合。在渲染程序中，它是表面各种可视属性的结合，这些可视属性是指表面的色彩、纹理、光滑度、透明度、反射率、折射率、发光度等。Three.js 给我们封装好了大部分的材质效果，避免我们使用复杂的 Shader 语言自己去实现。接下来我们先介绍下 Material 常用的一些属性和方法。</p><h3 id="基本属性和方法"><a href="#基本属性和方法" class="headerlink" title="基本属性和方法"></a>基本属性和方法</h3><h4 id="needsUpdate"><a href="#needsUpdate" class="headerlink" title="needsUpdate"></a>needsUpdate</h4><p>如果修改了 Material 内的内容，需要将 needsUpdate 属性设置为 true，Three.js 会在下一帧里将修改内容同步到 WebGL 的显存内。切记不要在 requestAnimationFrame 方法内更新，会浪费性能，只需要在更新 Material 属性后设置一次即可。</p><h4 id="side"><a href="#side" class="headerlink" title="side"></a>side</h4><p>此属性可以定义当前面的哪个方向会被渲染，默认值是 <code>THREE.FrontSide</code>（只渲染正面），可选值有：<code>THREE.BackSide</code>（只渲染背面）和 <code>THREE.DoubleSide</code>（正面和背面都会渲染）。</p><h4 id="transparent"><a href="#transparent" class="headerlink" title="transparent"></a>transparent</h4><p>此属性定义了材质是否可以透明，因为对于透明需要材质进行特殊处理，并在不透明的物体渲染完成后再渲染透明物体。当设置此属性为<code>true</code>后，可以通过设置<code>opacity</code>来调整透明度，默认为<code>false</code>。</p><h4 id="opacity"><a href="#opacity" class="headerlink" title="opacity"></a>opacity</h4><p>此属性可以定义材质的透明度，必须将材质的 transparent 设置为 true 才可使透明度管用。取值范围为 0.0 到 1.0。默认值是1.0，也就是不透明。</p><h4 id="map"><a href="#map" class="headerlink" title="map"></a>map</h4><p>此属性可以配置当前材质的纹理贴图，是一个 <code>THREE.Texture</code> 对象，下面我们将会讲解如何给材质贴图。这是大部分材质都会有的属性，只有极其个别的材质如 <code>LineBasicMaterial</code>（线材质）等没有这个属性。</p><h4 id="wireframe"><a href="#wireframe" class="headerlink" title="wireframe"></a>wireframe</h4><p>是否将模型渲染成线框，默认为 false。个别材质也没有这个属性。</p><h4 id="dispose-1"><a href="#dispose-1" class="headerlink" title="dispose()"></a>dispose()</h4><p>此方法用于将材质从内存中删除，在不需要使用当前材质时使用，但不会将材质的纹理贴图删除，如果需要将纹理贴图也删除，需要调用 <code>material.map.dispose()</code>。</p><h3 id="配置纹理贴图"><a href="#配置纹理贴图" class="headerlink" title="配置纹理贴图"></a>配置纹理贴图</h3><p>由于经常使用纹理贴图，所以在这里单独讲解一下如何实现一个纹理贴图。 实现纹理贴图有以下两种方式。</p><p>第一种，使用 <code>THREE.TextureLoader</code> 进行生成纹理对象：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">var</span> texture = <span class="hljs-keyword">new</span> THREE.TextureLoader().load( <span class="hljs-string">&quot;textures/water.jpg&quot;</span> ); <br>material.map = texture; <span class="hljs-comment">//将纹理赋值给材质</span><br></code></pre></td></tr></table></figure><p>或者直接实例化：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">var</span> texture = <span class="hljs-keyword">new</span> THREE.Texture(canvas); <span class="hljs-comment">//实例化的第一个对象可以是`img`、`canvas`和`video`。</span><br>material.map = texture; <span class="hljs-comment">//将纹理赋值给材质</span><br></code></pre></td></tr></table></figure><h4 id="纹理重复问题"><a href="#纹理重复问题" class="headerlink" title="纹理重复问题"></a>纹理重复问题</h4><p>如果图片不是标准的2的幂数（2、4、8、16、32、64、128、256、512、1024、2048……），在控制台会给我们提示：“THREE.WebGLRenderer: image is not power of two”，意思就是说图片不是标准格式高宽不是2的幂数。我们需要的水平方向和垂直方向上设置的图片重复显示。需要配置的两个属性是：<code>texture.wrapS</code>（水平方向重复）和 <code>texture.wrapT</code>（垂直方向重复），默认值是：<code>THREE.ClampToEdgeWrapping</code>，即纹理的最后一个像素延伸到网格的边缘。可选项有：<code>THREE.RepeatWrapping</code>，表示纹理将重复无穷大；<code>MirroredRepeatWrapping</code>，表示镜像重复，可以理解为重复时，反着绘制一个然后正着绘制一个，达到的效果就是没有强烈的过渡感觉。</p><h4 id="needsUpdate-属性"><a href="#needsUpdate-属性" class="headerlink" title="needsUpdate 属性"></a>needsUpdate 属性</h4><p>如果更新了纹理的相关属性，需要将此属性设置为 true，将数据同步到 WebGL。</p><h4 id="repeat"><a href="#repeat" class="headerlink" title="repeat"></a>repeat</h4><p>纹理在整个表面水平方向和垂直方向重复多少次，也会受纹理重复设置的影响，设置方式为：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">var</span> texture = <span class="hljs-keyword">new</span> THREE.TextureLoader().load( <span class="hljs-string">&quot;textures/water.jpg&quot;</span> );<br>texture.wrapS = THREE.RepeatWrapping; <span class="hljs-comment">//设置水平方向无限循环</span><br>texture.wrapT = THREE.RepeatWrapping; <span class="hljs-comment">//设置垂直方向无限循环</span><br>texture.repeat.set( <span class="hljs-number">4</span>, <span class="hljs-number">4</span> ); <span class="hljs-comment">//水平方向和垂直方向都重复四次</span><br></code></pre></td></tr></table></figure><h3 id="内置常用材质"><a href="#内置常用材质" class="headerlink" title="内置常用材质"></a>内置常用材质</h3><p>在讲解常用材质之前，我们先讲解一下如何实例化一个材质和一些需要注意的地方。我们使用第一个讲到的材质 MeshBasicMaterial 作为例子。</p><h4 id="MeshBasicMaterial-和设置颜色的方法"><a href="#MeshBasicMaterial-和设置颜色的方法" class="headerlink" title="MeshBasicMaterial 和设置颜色的方法"></a>MeshBasicMaterial 和设置颜色的方法</h4><p>这种材质是一种简单的材质，不会受到光的影响，直接看到的效果就是整个物体的颜色都是一样，没有立体的感觉。在实例化材质时，我们可以传入一个对象，设置材质的相关属性可以通过对象属性的方式传入，但是属性 color（颜色）例外，实例化的时候可以传入十六进制数，也可以写十六进制字符串。实例化完成后再修改需要重新赋值 <code>THREE.Color</code> 对象，或者调用 <code>material.color.set</code> 方法赋值。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">var</span> material = <span class="hljs-keyword">new</span> THREE.MeshBasicMaterial(&#123;<span class="hljs-attr">color</span>:<span class="hljs-number">0x00ffff</span>&#125;);<br><span class="hljs-keyword">var</span> geometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>);<br><br><span class="hljs-keyword">var</span> mesh = <span class="hljs-keyword">new</span> THREE.Mesh(geometry, material);<br>scene.add(mesh);<br></code></pre></td></tr></table></figure><p>上面的案例就是使用 MeshBasicMaterial 材质创建了一个立方体，我们设置了显示颜色为一种浅蓝色，除了上面实例化的时候进行设置，后面也可以再修改：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">var</span> material = <span class="hljs-keyword">new</span> THREE.MeshBasicMaterial(&#123;<span class="hljs-attr">color</span>:<span class="hljs-number">0x00ffff</span>&#125;); <span class="hljs-comment">//设置初始的颜色为浅蓝色</span><br>material.color.set(<span class="hljs-number">0xff00ff</span>); <span class="hljs-comment">//将颜色修改为紫色</span><br></code></pre></td></tr></table></figure><p>我们也可以直接赋值一个新的 <code>THREE.Color</code> 对象，如：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">var</span> material = <span class="hljs-keyword">new</span> THREE.MeshBasicMaterial(&#123;<span class="hljs-attr">color</span>:<span class="hljs-number">0x00ffff</span>&#125;); <span class="hljs-comment">//设置初始的颜色为浅蓝色</span><br>material.color = <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-number">0xff00ff</span>); <span class="hljs-comment">//将颜色修改为紫色</span><br></code></pre></td></tr></table></figure><p>我们可以通过 <code>new THREE.Color</code> 创建一个颜色对象，Three.js 支持的颜色书写方式比较丰富，如：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">//直接传入十六进制数或者字符串</span><br><span class="hljs-keyword">var</span> color = <span class="hljs-keyword">new</span> THREE.Color( <span class="hljs-number">0xff0000</span> );<br><span class="hljs-keyword">var</span> color = <span class="hljs-keyword">new</span> THREE.Color( <span class="hljs-string">&quot;#ff0000&quot;</span> );<br><br><span class="hljs-comment">//RGB 字符串</span><br><span class="hljs-keyword">var</span> color = <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">&quot;rgb(255, 0, 0)&quot;</span>);<br><span class="hljs-keyword">var</span> color = <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">&quot;rgb(100%, 0%, 0%)&quot;</span>);<br><br><span class="hljs-comment">//支持一百四十多中颜色名称</span><br><span class="hljs-keyword">var</span> color = <span class="hljs-keyword">new</span> THREE.Color( <span class="hljs-string">&#x27;skyblue&#x27;</span> );<br><br><span class="hljs-comment">//HSL 字符串</span><br><span class="hljs-keyword">var</span> color = <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-string">&quot;hsl(0, 100%, 50%)&quot;</span>);<br><br><span class="hljs-comment">//支持RGB值设置在0到1之间的方式</span><br><span class="hljs-keyword">var</span> color = <span class="hljs-keyword">new</span> THREE.Color( <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span> );<br></code></pre></td></tr></table></figure><h4 id="MeshNormalMaterial-法向材质"><a href="#MeshNormalMaterial-法向材质" class="headerlink" title="MeshNormalMaterial 法向材质"></a>MeshNormalMaterial 法向材质</h4><p>这种材质会根据面的方向不同自动改变颜色，也是我们之前一直在用的材质。此材质不受灯光影响。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js">geometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry( <span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span> ); <span class="hljs-comment">//创建几何体</span><br>material = <span class="hljs-keyword">new</span> THREE.MeshNormalMaterial(); <span class="hljs-comment">//创建材质</span><br><br>mesh = <span class="hljs-keyword">new</span> THREE.Mesh( geometry, material ); <span class="hljs-comment">//创建网格</span><br>scene.add( mesh ); <span class="hljs-comment">//将网格添加到场景</span><br></code></pre></td></tr></table></figure><h4 id="LineBasicMaterial-线条材质"><a href="#LineBasicMaterial-线条材质" class="headerlink" title="LineBasicMaterial 线条材质"></a>LineBasicMaterial 线条材质</h4><p>在上一篇我们讲几何体时，没有讲解如何画直线，是由于直线需要单独的材质进行实现，所以我们将直线放到了材质这一篇中进行讲解。注意，由于 Windows 系统的原因，线的宽度只能为1。</p><p>要绘制线段，我们需要确定两个点，就是起点和终点，案例中我们使用了四个顶点创建了三条线。然后 Geometry 对象使用这组顶点配置几何体，实例化线的材质，最后使用 <code>THREE.Line</code> 生成线。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">//添加直线</span><br><span class="hljs-keyword">var</span> pointsArr = [<br>    <span class="hljs-keyword">new</span> THREE.Vector3( -<span class="hljs-number">10</span>, <span class="hljs-number">0</span>, -<span class="hljs-number">5</span> ),<br>    <span class="hljs-keyword">new</span> THREE.Vector3( -<span class="hljs-number">5</span>, <span class="hljs-number">15</span>, <span class="hljs-number">5</span> ),<br>    <span class="hljs-keyword">new</span> THREE.Vector3( <span class="hljs-number">20</span>, <span class="hljs-number">15</span>, -<span class="hljs-number">5</span> ),<br>    <span class="hljs-keyword">new</span> THREE.Vector3( <span class="hljs-number">10</span>, <span class="hljs-number">0</span>, <span class="hljs-number">5</span> )<br>];<br><br><span class="hljs-keyword">var</span> lineGeometry = <span class="hljs-keyword">new</span> THREE.Geometry(); <span class="hljs-comment">//实例化几何体</span><br>lineGeometry.setFromPoints(pointsArr); <span class="hljs-comment">//使用当前点的属性配置几何体</span><br><br><span class="hljs-keyword">var</span> lineMaterial = <span class="hljs-keyword">new</span> THREE.LineBasicMaterial(&#123;<span class="hljs-attr">color</span>:<span class="hljs-number">0x00ff00</span>&#125;); <span class="hljs-comment">//材质</span><br><br>line = <span class="hljs-keyword">new</span> THREE.Line(lineGeometry, lineMaterial);<br>scene.add(line);<br></code></pre></td></tr></table></figure><h4 id="LineDashedMaterial-虚线"><a href="#LineDashedMaterial-虚线" class="headerlink" title="LineDashedMaterial 虚线"></a>LineDashedMaterial 虚线</h4><p>我们也可以创建虚线，这里我们来点新花样，就是实现曲线。曲线也和直线一样，在 Windows 系统线的粗度只能为1。</p><p>要创建曲线，我们需要使用到 <code>THREE.CatmullRomCurve3</code> 来生成一个 curve 对象，这是一个曲线对象，可以从对象获取生成的曲线的点的集合，在这里科普一下，曲线也是由无数段的直线组成的，段数分的越清晰，曲线过渡越顺滑。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">var</span> pointsArr = [<br>    <span class="hljs-keyword">new</span> THREE.Vector3( -<span class="hljs-number">10</span>, <span class="hljs-number">0</span>, -<span class="hljs-number">5</span> ),<br>    <span class="hljs-keyword">new</span> THREE.Vector3( -<span class="hljs-number">5</span>, <span class="hljs-number">15</span>, <span class="hljs-number">5</span> ),<br>    <span class="hljs-keyword">new</span> THREE.Vector3( <span class="hljs-number">20</span>, <span class="hljs-number">15</span>, -<span class="hljs-number">5</span> ),<br>    <span class="hljs-keyword">new</span> THREE.Vector3( <span class="hljs-number">10</span>, <span class="hljs-number">0</span>, <span class="hljs-number">5</span> )<br>];<br><span class="hljs-comment">//指定一些用于生成曲线线的三维顶点</span><br><span class="hljs-keyword">var</span> curve = <span class="hljs-keyword">new</span> THREE.CatmullRomCurve3(pointsArr);<br><br><span class="hljs-keyword">var</span> points = curve.getPoints( <span class="hljs-number">50</span> ); <span class="hljs-comment">//使用getPoints获取当前曲线分成50段后的所有顶点</span><br><span class="hljs-keyword">var</span> curveGeometry = <span class="hljs-keyword">new</span> THREE.BufferGeometry().setFromPoints( points ); <span class="hljs-comment">//使用顶点生成几何体</span><br><br><span class="hljs-keyword">var</span> curveMaterial = <span class="hljs-keyword">new</span> THREE.LineDashedMaterial( &#123; <span class="hljs-attr">color</span> : <span class="hljs-number">0xff0000</span> &#125; ); <span class="hljs-comment">//创建一条红色的线材质</span><br><br><span class="hljs-comment">// 使用THREE.Line创建线</span><br>curveLine = <span class="hljs-keyword">new</span> THREE.Line( curveGeometry, curveMaterial );<br>curveLine.computeLineDistances(); <span class="hljs-comment">//需要重新计算位置才能显示出虚线</span><br>scene.add(curveLine);<br></code></pre></td></tr></table></figure><h4 id="添加光"><a href="#添加光" class="headerlink" title="添加光"></a>添加光</h4><p>由于 MeshBasicMaterial 不会受光的影响，即使有光也不会影响它的效果，前面我们也没有添加光。但是后面介绍的材质会受到光源的影响，在介绍之前，我们需要添加一个光源，来影响材质的显示效果。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">//创建灯光</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initLight</span>(<span class="hljs-params"></span>) </span>&#123;<br>    <span class="hljs-keyword">var</span> light = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>); <span class="hljs-comment">//添加了一个白色的平行光</span><br>    light.position.set(<span class="hljs-number">20</span>, <span class="hljs-number">50</span>, <span class="hljs-number">50</span>); <span class="hljs-comment">//设置光的方向</span><br>    scene.add(light); <span class="hljs-comment">//添加到场景</span><br><br>    <span class="hljs-comment">//添加一个全局环境光</span><br>    scene.add(<span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0x222222</span>));<br>&#125;<br></code></pre></td></tr></table></figure><p>上面我们添加了一个模拟太阳光线的平行光和一个对每一个物理都造成影响的环境光，具体的内容将会在下一篇讲解。</p><p>下面介绍的材质都是对光有反应的，而且如果场景内没有光，模型将无法显示。</p><h4 id="MeshLambertMaterial-兰伯特材质"><a href="#MeshLambertMaterial-兰伯特材质" class="headerlink" title="MeshLambertMaterial 兰伯特材质"></a>MeshLambertMaterial 兰伯特材质</h4><p>这种材质会对光有反应，但是不会出现高光，可以模拟一些粗糙的材质的物体，比如木头或者石头。</p><p>实现案例，如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js">geometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry( <span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span> ); <span class="hljs-comment">//创建几何体</span><br>material = <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial(&#123;<span class="hljs-attr">color</span>:<span class="hljs-number">0x00ffff</span>&#125;); <span class="hljs-comment">//创建材质</span><br><br>mesh = <span class="hljs-keyword">new</span> THREE.Mesh( geometry, material ); <span class="hljs-comment">//创建网格</span><br>scene.add( mesh ); <span class="hljs-comment">//将网格添加到场景</span><br></code></pre></td></tr></table></figure><h4 id="MeshPhongMaterial-高光材质"><a href="#MeshPhongMaterial-高光材质" class="headerlink" title="MeshPhongMaterial 高光材质"></a>MeshPhongMaterial 高光材质</h4><p>这种材质具有高光效果，可以模拟一些光滑的物体的材质效果，比如油漆面，瓷瓦等光滑物体。</p><p>实现案例如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js">geometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry( <span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span> ); <span class="hljs-comment">//创建几何体</span><br>material = <span class="hljs-keyword">new</span> THREE.MeshPhongMaterial(&#123;<span class="hljs-attr">color</span>:<span class="hljs-number">0x00ffff</span>&#125;); <span class="hljs-comment">//创建材质</span><br><br>mesh = <span class="hljs-keyword">new</span> THREE.Mesh( geometry, material ); <span class="hljs-comment">//创建网格</span><br>scene.add( mesh ); <span class="hljs-comment">//将网格添加到场景</span><br></code></pre></td></tr></table></figure><h4 id="MeshStandardMaterial-基于物理的渲染（PBR）材质"><a href="#MeshStandardMaterial-基于物理的渲染（PBR）材质" class="headerlink" title="MeshStandardMaterial 基于物理的渲染（PBR）材质"></a>MeshStandardMaterial 基于物理的渲染（PBR）材质</h4><p>这种材质基于物理的渲染（PBR）材质，生成的材质效果更佳，但是相应也占用更多的计算量。这种材质我们可以定义它的粗糙度来确定反光效果，经常用于模拟金属的质感，使金属质感更加真实。</p><p>实现案例如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs js">geometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry( <span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span> ); <span class="hljs-comment">//创建几何体</span><br>material = <span class="hljs-keyword">new</span> THREE.MeshPhongMaterial(&#123;<span class="hljs-attr">color</span>:<span class="hljs-number">0x00ffff</span>&#125;); <span class="hljs-comment">//创建材质</span><br>material.metalness = <span class="hljs-number">0.1</span>; <span class="hljs-comment">//设置的值的范围为0-1，值越小，材质越光滑，高光越明显</span><br>material.metalnessMap = <span class="hljs-number">0.1</span>; <span class="hljs-comment">//设置的值的范围为0-1，值越大，越有生锈金属的质感，值越小反光越清晰</span><br><br>mesh = <span class="hljs-keyword">new</span> THREE.Mesh( geometry, material ); <span class="hljs-comment">//创建网格</span><br>scene.add( mesh ); <span class="hljs-comment">//将网格添加到场景</span><br></code></pre></td></tr></table></figure><h3 id="案例代码"><a href="#案例代码" class="headerlink" title="案例代码"></a>案例代码</h3><h2 id="相机Camera"><a href="#相机Camera" class="headerlink" title="相机Camera"></a>相机Camera</h2><p>相机是 Three.js 抽象出来的一个对象，使用此对象，我们可以定义显示的内容，并且可以通过移动相机位置来显示不同的内容。</p><p>下面讲解一下 Three.js 中相机的通用属性和常用的相机对象。</p><h3 id="相机通用属性和方法"><a href="#相机通用属性和方法" class="headerlink" title="相机通用属性和方法"></a>相机通用属性和方法</h3><p>我们常用的相机有正交相机（OrthographicCamera）和透视相机（PerspectiveCamera）两种，用于来捕获场景内显示的物体模型。它们有一些通用的属性和方法。</p><h4 id="Object3D-的所有属性和方法"><a href="#Object3D-的所有属性和方法" class="headerlink" title="Object3D 的所有属性和方法"></a>Object3D 的所有属性和方法</h4><p>由于相机都继承自 THREE.Object3D 对象，所以像设置位置的 position 属性、rotation 旋转和 scale 缩放属性，可以直接对相机对象设置。我们甚至还可以使用 <code>add()</code> 方法，给相机对象添加子类，移动相机它的子类也会跟随着一块移动，我们可以使用这个特性制作一些比如 HUD 类型的显示界面。</p><h4 id="target-焦点属性和-lookAt-方法"><a href="#target-焦点属性和-lookAt-方法" class="headerlink" title="target 焦点属性和 lookAt() 方法"></a>target 焦点属性和 lookAt() 方法</h4><p>这两个方法的效果一定，都是调整相机的朝向，可以设置一个 <code>THREE.Vector3</code>（三维向量）点的位置：</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">camera</span>.target = new THREE.Vector<span class="hljs-number">3</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);<br><span class="hljs-attribute">camera</span>.lookAt(new THREE.Vector<span class="hljs-number">3</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>));<br></code></pre></td></tr></table></figure><p>上面两个都是朝向了原点，我们也可以将相机的朝向改为模型网格的 position，如果物体的位置发生了变化，相机的焦点方向也会跟随变动：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">var mesh = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">Mesh(<span class="hljs-params">geometry</span>, <span class="hljs-params">material</span>)</span>;<br>camera.target = mesh.position;<br><span class="hljs-comment">//或者</span><br>camera.look<span class="hljs-constructor">At(<span class="hljs-params">mesh</span>.<span class="hljs-params">position</span>)</span>;<br></code></pre></td></tr></table></figure><h4 id="getWorldDirection"><a href="#getWorldDirection" class="headerlink" title="getWorldDirection()"></a>getWorldDirection()</h4><p>getWorldDirection() 方法可以获取当前位置到 target 位置的世界中的方向。方向也可以使用 <code>THREE.Vector3</code> 对象表示，所以该方法返回一个三维向量。</p><h3 id="OrthographicCamera-正交相机"><a href="#OrthographicCamera-正交相机" class="headerlink" title="OrthographicCamera 正交相机"></a>OrthographicCamera 正交相机</h3><p>使用正交相机 OrthographicCamera 渲染出来的场景，所有的物体和模型都按照它固有的尺寸和精度显示，一般使用在工业要求精度或者 2D 平面中，因为它能完整的显示物体应有的尺寸。</p><h4 id="创建正交相机"><a href="#创建正交相机" class="headerlink" title="创建正交相机"></a>创建正交相机</h4><img src="/blog/posts/89cc5dea/54accde0-7573-11e8-ae69-7f0c27c81098" class="" title="OrthographicCamera"><p>上面的图片可以清晰的显示出正交相机显示的范围，它显示的内容是一个立方体结构，通过图片我们发现，只要确定 top、left、right、bottom、near 和 far 六个值，我们就能确定当前相机捕获场景的区域，在这个区域外面的内容不会被渲染，所以，我们创建相机的方法就是：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs reasonml"><span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">OrthographicCamera( <span class="hljs-params">left</span>, <span class="hljs-params">right</span>, <span class="hljs-params">top</span>, <span class="hljs-params">bottom</span>, <span class="hljs-params">near</span>, <span class="hljs-params">far</span> )</span>;<br></code></pre></td></tr></table></figure><p>下面我们创建了一个显示场景中相机位置前方长宽高都为4的盒子内的物体的正交相机：</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">var</span> orthographicCamera = new THREE.OrthographicCamera(-<span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>, -<span class="hljs-number">2</span>, <span class="hljs-number">0</span>, <span class="hljs-number">4</span>);<br><span class="hljs-attribute">scene</span>.add(orthographicCamera); //一般不需要将相机放置到场景当中，如果需要添加子元素等一些特殊操作，还是需要add到场景内<br></code></pre></td></tr></table></figure><p>正常情况相机显示的内容需要和窗口显示的内容为同样的比例才能够显示没有被拉伸变形的效果：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">var frustumSize = <span class="hljs-number">1000</span>; <span class="hljs-comment">//设置显示相机前方1000高的内容</span><br>var aspect = window.innerWidth<span class="hljs-operator"> / </span>window.innerHeight; <span class="hljs-comment">//计算场景的宽高比</span><br>var orthographicCamera = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">OrthographicCamera( <span class="hljs-params">frustumSize</span> <span class="hljs-operator">*</span> <span class="hljs-params">aspect</span> <span class="hljs-operator">/</span> - 2, <span class="hljs-params">frustumSize</span> <span class="hljs-operator">*</span> <span class="hljs-params">aspect</span> <span class="hljs-operator">/</span> 2, <span class="hljs-params">frustumSize</span> <span class="hljs-operator">/</span> 2, <span class="hljs-params">frustumSize</span> <span class="hljs-operator">/</span> - 2, 1, 2000 )</span>; <span class="hljs-comment">//根据比例计算出left，top，right，bottom的值</span><br></code></pre></td></tr></table></figure><h4 id="动态修改正交相机属性"><a href="#动态修改正交相机属性" class="headerlink" title="动态修改正交相机属性"></a>动态修改正交相机属性</h4><p>我们也可以动态的修改正交相机的一些属性，注意修改完以后需要调用相机 updateProjectionMatrix() 方法来更新相机显存里面的内容，代码如下：</p><figure class="highlight oxygene"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs oxygene"><span class="hljs-keyword">var</span> frustumSize = <span class="hljs-number">1000</span>; <span class="hljs-comment">//设置显示相机前方1000高的内容</span><br><span class="hljs-keyword">var</span> <span class="hljs-keyword">aspect</span> = window.innerWidth / window.innerHeight; <span class="hljs-comment">//计算场景的宽高比</span><br><span class="hljs-keyword">var</span> orthographicCamera = <span class="hljs-keyword">new</span> THREE.OrthographicCamera(); <span class="hljs-comment">//实例化一个空的正交相机</span><br>orthographicCamera.left = frustumSize * <span class="hljs-keyword">aspect</span> / - <span class="hljs-number">2</span>; <span class="hljs-comment">//设置left的值</span><br>orthographicCamera.right = frustumSize * <span class="hljs-keyword">aspect</span> / <span class="hljs-number">2</span>; <span class="hljs-comment">//设置right的值</span><br>orthographicCamera.top = frustumSize / <span class="hljs-number">2</span>; <span class="hljs-comment">//设置top的值</span><br>orthographicCamera.bottom = frustumSize / - <span class="hljs-number">2</span>; <span class="hljs-comment">//设置bottom的值</span><br>orthographicCamera.near = <span class="hljs-number">1</span>; <span class="hljs-comment">//设置near的值</span><br>orthographicCamera.far = <span class="hljs-number">2000</span>; <span class="hljs-comment">//设置far的值</span><br><br><span class="hljs-comment">//注意，最后一定要调用updateProjectionMatrix()方法更新</span><br>orthographicCamera.updateProjectionMatrix();<br></code></pre></td></tr></table></figure><h4 id="窗口变动后的更新"><a href="#窗口变动后的更新" class="headerlink" title="窗口变动后的更新"></a>窗口变动后的更新</h4><p>由于浏览器的窗口可以随意修改，我们有时候需要监听浏览器窗口的变化，然后获取到最新的宽高比，再重新设置相关属性：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">var aspect = window.innerWidth<span class="hljs-operator"> / </span>window.innerHeight; <span class="hljs-comment">//重新获取场景的宽高比</span><br><br><span class="hljs-comment">//重新设置left right top bottom 四个值</span><br>orthographicCamera.left = frustumSize<span class="hljs-operator"> * </span>aspect<span class="hljs-operator"> / </span>- <span class="hljs-number">2</span>; <span class="hljs-comment">//设置left的值</span><br>orthographicCamera.right = frustumSize<span class="hljs-operator"> * </span>aspect<span class="hljs-operator"> / </span><span class="hljs-number">2</span>; <span class="hljs-comment">//设置right的值</span><br>orthographicCamera.top = frustumSize<span class="hljs-operator"> / </span><span class="hljs-number">2</span>; <span class="hljs-comment">//设置top的值</span><br>orthographicCamera.bottom = frustumSize<span class="hljs-operator"> / </span>- <span class="hljs-number">2</span>; <span class="hljs-comment">//设置bottom的值</span><br><br><span class="hljs-comment">//最后，记得一定要更新数据</span><br>orthographicCamera.update<span class="hljs-constructor">ProjectionMatrix()</span>;<br><br><span class="hljs-comment">//显示区域尺寸变了，我们也需要修改渲染器的比例</span><br>renderer.set<span class="hljs-constructor">Size(<span class="hljs-params">window</span>.<span class="hljs-params">innerWidth</span>, <span class="hljs-params">window</span>.<span class="hljs-params">innerHeight</span>)</span>;<br></code></pre></td></tr></table></figure><h3 id="PerspectiveCamera-透视相机"><a href="#PerspectiveCamera-透视相机" class="headerlink" title="PerspectiveCamera 透视相机"></a>PerspectiveCamera 透视相机</h3><p>透视相机是最常用的也是模拟人眼视角的一种相机，它所渲染生成的页面是一种近大远小的效果。</p><h4 id="创建透视相机"><a href="#创建透视相机" class="headerlink" title="创建透视相机"></a>创建透视相机</h4><img src="/blog/posts/89cc5dea/e27d7170-7577-11e8-a337-9bdf53091bce" class="" title="PerspectiveCamera"><p>上面的图片就是一个透视相机的生成原理，我们先看看渲染的范围是如何生成的：</p><ul><li>首先，我们需要确定一个 fov 值，这个值是用来确定相机前方的垂直视角，角度越大，我们能够查看的内容就越多。</li><li>然后，我们又确定了一个渲染的宽高比，这个宽高比最好设置成页面显示区域的宽高比，这样我们查看生成画面才不会出现拉伸变形的效果，这时，我们可以确定前面生成内容的范围是一个四棱锥的区域。</li><li>最后，我们需要确定的就是相机渲染范围的最小值 near 和最大值 far，注意，这两个值都是距离相机的距离，确定完数值后，相机会显示的范围就是一个近小远大的四棱柱的范围，我们能够看到的内容都是在这个范围内的。</li></ul><p>通过上面的原理，我们需要设置 fov 垂直角度，aspect 视角宽高比例和 near 最近渲染距离 far 最远渲染距离，就能确定当前透视相机的渲染范围。</p><p>下面代码展示了一个透视相机的创建方法：</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs maxima"><span class="hljs-built_in">var</span> perspectiveCamera = <span class="hljs-built_in">new</span> THREE.PerspectiveCamera( <span class="hljs-number">45</span>, <span class="hljs-built_in">width</span> / <span class="hljs-built_in">height</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1000</span> );<br><span class="hljs-built_in">scene</span>.add( perspectiveCamera );<br></code></pre></td></tr></table></figure><p>我们设置了前方的视角为45度，宽度和高度设置成显示窗口的宽度除以高度的比例即可，显示距离为1到1000距离以内的物体。</p><h4 id="动态修改透视相机的属性"><a href="#动态修改透视相机的属性" class="headerlink" title="动态修改透视相机的属性"></a>动态修改透视相机的属性</h4><p>透视相机的属性创建完成后我们也可以根据个人需求随意修改，但是注意，相机的属性修改完成后，以后要调用 <code>updateProjectionMatrix()</code> 方法来更新：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">var perspectiveCamera = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">PerspectiveCamera( 45, <span class="hljs-params">width</span> <span class="hljs-operator">/</span> <span class="hljs-params">height</span>, 1, 1000 )</span>;<br>scene.add( perspectiveCamera );<br><br><span class="hljs-comment">//下面为修改当前相机属性</span><br>perspectiveCamera.fov = <span class="hljs-number">75</span>; <span class="hljs-comment">//修改相机的fov</span><br>perspectiveCamera.aspect = window.innerWidth/window.innerHeight; <span class="hljs-comment">//修改相机的宽高比</span><br>perspectiveCamera.near = <span class="hljs-number">100</span>; <span class="hljs-comment">//修改near</span><br>perspectiveCamera.far = <span class="hljs-number">500</span>; <span class="hljs-comment">//修改far</span><br><br><span class="hljs-comment">//最后更新</span><br>perspectiveCamera.update<span class="hljs-constructor">ProjectionMatrix()</span>;<br></code></pre></td></tr></table></figure><h4 id="显示窗口变动后的回调"><a href="#显示窗口变动后的回调" class="headerlink" title="显示窗口变动后的回调"></a>显示窗口变动后的回调</h4><p>如果当前场景浏览器的显示窗口变动了，比如修改了浏览器的宽高后，我们需要设置场景自动更新，下面是一个常用的案例：</p><figure class="highlight mel"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs mel">function onWindowResize() &#123;<br>    <span class="hljs-keyword">camera</span>.aspect = <span class="hljs-keyword">window</span>.innerWidth / <span class="hljs-keyword">window</span>.innerHeight; <span class="hljs-comment">//重新设置宽高比</span><br>    <span class="hljs-keyword">camera</span>.updateProjectionMatrix(); <span class="hljs-comment">//更新相机</span><br>    <span class="hljs-keyword">renderer</span>.setSize(<span class="hljs-keyword">window</span>.innerWidth, <span class="hljs-keyword">window</span>.innerHeight); <span class="hljs-comment">//更新渲染页面大小</span><br>&#125;<br><span class="hljs-keyword">window</span>.onresize = onWindowResize;<br></code></pre></td></tr></table></figure><p>最后，我写了一个案例来查看透视相机和正交相机的显示区别：<a href="https://johnson2heng.github.io/GitChat-Three.js/07第七节 camera/index.html">点击这里案例 Demo</a>。</p><p>左侧是透视相机显示的效果，这种更符合人眼看到的效果，场景更加的立体。而右侧则是正交相机实现的效果，渲染出来的数值更加准确，但是却不符合人眼查看的效果。</p><p>案例代码查看地址：<a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/07第七节 camera/index.html">点击这里</a>。</p><h4 id="制作相机控制器"><a href="#制作相机控制器" class="headerlink" title="制作相机控制器"></a>制作相机控制器</h4><p>在实际生产当中，很多时候我们需要切换相机的位置，以达到项目需求。 Three.js 也有很多相机控制插件，这个我们会在后面的课程当中讲解。</p><p>下面是我书写的一个小案例，能够通过鼠标左键拖拽界面让相机围绕模型周围查看模型。</p><p>首先，先上案例地址：<a href="https://johnson2heng.github.io/GitChat-Three.js/07第七节 camera/control.html">点击这里</a>。</p><p>代码查看地址：<a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/07第七节 camera/control.html">点击这里</a>。</p><p>其实和以前的代码相比，我们也只是多出来了一个 initControl 方法，在这个方法里面绑定鼠标事件。实现的思路也很简单：</p><ul><li>首先绑定鼠标按下事件，获取到按下时的相机位置和距离原点的距离，计算出来相对于 Z 轴正方向的偏移。绑定鼠标移动事件。</li><li>然后，在鼠标移动事件里面获取到距离鼠标按下的偏移，通过鼠标偏移数值计算出现在相机位置，并赋值。</li></ul><p>思路就这么简单，虽然实现起来会麻烦，我也尽量写的简单，大家尽量能够自己也写出来。</p><h2 id="粒子Points"><a href="#粒子Points" class="headerlink" title="粒子Points"></a>粒子Points</h2><p>我们将学习到 Sprite 精灵和 Points 粒子，这两种对象共同点就是我们通过相机查看它们时，始终看到的是它们的正面，它们总朝向相机。通过它们的这种特性，我们可以实现广告牌的效果，或实现更多的比如雨雪、烟雾等更加绚丽的特效。</p><h3 id="Sprite-精灵"><a href="#Sprite-精灵" class="headerlink" title="Sprite 精灵"></a>Sprite 精灵</h3><p>精灵由于一直正对着相机的特性，一般使用在模型的提示信息当中。通过 <code>THREE.Sprite</code> 创建生成，由于 <code>THREE.Sprite</code> 和 <code>THREE.Mesh</code> 都属于 <code>THREE.Object3D</code> 的子类，所以，我们操作模型网格的相关属性和方法大部分对于精灵都适用。和精灵一起使用的还有一个 <code>THREE.SpriteMaterial</code> 对象，它专门配合精灵的材质。注意，精灵没有阴影效果。</p><p>下面首先创建一个最简单的精灵：</p><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs haxe"><span class="hljs-keyword">var</span> spriteMaterial = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.SpriteMaterial( &#123; color: <span class="hljs-type">0xffffff </span>&#125; );<br><span class="hljs-keyword">var</span> sprite = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Sprite( spriteMaterial );<br>scene.add( sprite );<br></code></pre></td></tr></table></figure><p>创建一个带有纹理图片的精灵：</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs maxima"><span class="hljs-built_in">var</span> spriteMap = <span class="hljs-built_in">new</span> THREE.TextureLoader().<span class="hljs-built_in">load</span>( <span class="hljs-string">&quot;sprite.png&quot;</span> );<br><span class="hljs-built_in">var</span> spriteMaterial = <span class="hljs-built_in">new</span> THREE.SpriteMaterial( &#123; <span class="hljs-built_in">map</span>: spriteMap, <span class="hljs-built_in">color</span>: <span class="hljs-number">0xffffff</span> &#125; );<br><span class="hljs-built_in">var</span> sprite = <span class="hljs-built_in">new</span> THREE.Sprite( spriteMaterial );<br><span class="hljs-built_in">scene</span>.add( sprite );<br></code></pre></td></tr></table></figure><p>直接使用 canvas 创建精灵：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">var spriteMap = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">Texture(<span class="hljs-params">canvas</span>)</span>;<br>var spriteMaterial = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">SpriteMaterial( &#123; <span class="hljs-params">map</span>: <span class="hljs-params">spriteMap</span>, <span class="hljs-params">color</span>: 0xffffff &#125; )</span>;<br>var sprite = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">Sprite( <span class="hljs-params">spriteMaterial</span> )</span>;<br>scene.add( sprite );<br></code></pre></td></tr></table></figure><h4 id="相关属性"><a href="#相关属性" class="headerlink" title="相关属性"></a>相关属性</h4><p>精灵特有的属性就一个，即 center 属性，值是一个二维向量 <code>THREE.Vector2</code>，这个属性意义就是当前设置的精灵位置点的位置处于精灵图中的位置。如果 center 的值是 <code>0,0</code>旋转的位置就在左下角，如果值为 <code>1,1</code> 的话，那旋转的位置则在精灵图的右上角，默认值是 <code>0.5,0.5</code>：</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">sprite</span>.center.set(<span class="hljs-number">0</span>.<span class="hljs-number">5</span>, <span class="hljs-number">0</span>); //设置位置点处于精灵的最下方中间位置<br></code></pre></td></tr></table></figure><p>接下来，我们查看一下精灵的案例：<a href="https://johnson2heng.github.io/GitChat-Three.js/08第八节 points/sprite.html">点击这里</a>。</p><p>案例代码查看地址：<a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/08第八节 points/sprite.html">点击这里</a>。</p><p>案例效果从左到右依次是，普通的精灵，贴图纹理的精灵和 <code>canvas</code> 创建的精灵。</p><h3 id="points-粒子"><a href="#points-粒子" class="headerlink" title="points 粒子"></a>points 粒子</h3><p>粒子和精灵的效果是一样的，它们之间的区别是，如果当前场景内的精灵过多的话，就会出现性能问题。粒子的作用就是为解决很多精灵而出现的，我们可以使用粒子去模型数量很多的效果，比如下雨，下雪等，数量很多的时候就适合使用粒子来创建，相应的，提高性能的损失就是失去了对单个精灵的操作，所有的粒子的效果都是一样。总的来说，粒子就是提高性能减少的一些自由度，而精灵就是为了自由度而损失了一些性能。</p><h4 id="粒子的创建"><a href="#粒子的创建" class="headerlink" title="粒子的创建"></a>粒子的创建</h4><p>粒子 <code>THREE.Points</code> 和精灵 <code>THREE.Sprite</code> 还有网格 <code>THREE.Mesh</code> 都属于 <code>THREE.Object3D</code> 的一个扩展，但是粒子有一些特殊的情况就是 <code>THREE.Points</code> 是它们粒子个体的父元素，它的位置设置也是基于 <code>THREE.Points</code> 位置而定位，而修改 <code>THREE.Points</code> 的 scale 属性只会修改掉粒子个体的位置。下面我们看下一个粒子的创建方法。创建一个粒子，需要一个含有顶点的几何体，和粒子纹理 <code>THREE.PointsMaterial</code> 的创建：</p><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs haxe"><span class="hljs-comment">//球体</span><br><span class="hljs-keyword">var</span> sphereGeometry = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.SphereGeometry(<span class="hljs-number">5</span>, <span class="hljs-number">24</span>, <span class="hljs-number">16</span>);<br><span class="hljs-keyword">var</span> sphereMaterial = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.PointsMaterial(&#123;color: <span class="hljs-type">0xff00ff</span>&#125;);<br><span class="hljs-keyword">var</span> sphere = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.Points(sphereGeometry, sphereMaterial);<br>scene.add(sphere);<br></code></pre></td></tr></table></figure><p>上面是通过球体几何体创建的一个最简单的粒子特效。</p><p>使用任何几何体都可以，甚至自己生成的几何体都可以，比如创建星空的案例：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">var starsGeometry = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">Geometry()</span>;<br><span class="hljs-comment">//生成一万个点的位置</span><br><span class="hljs-keyword">for</span> (var i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10000</span>; i++) &#123;<br>    var star = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">Vector3()</span>;<br>    <span class="hljs-comment">//THREE.Math.randFloatSpread 在区间内随机浮动* - 范围 / 2 *到* 范围 / 2 *内随机取值。</span><br>    star.x = <span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">THREE</span>.</span><span class="hljs-module"><span class="hljs-identifier">Math</span>.</span></span>rand<span class="hljs-constructor">FloatSpread(2000)</span>;<br>    star.y = <span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">THREE</span>.</span><span class="hljs-module"><span class="hljs-identifier">Math</span>.</span></span>rand<span class="hljs-constructor">FloatSpread(2000)</span>;<br>    star.z = <span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">THREE</span>.</span><span class="hljs-module"><span class="hljs-identifier">Math</span>.</span></span>rand<span class="hljs-constructor">FloatSpread(2000)</span>;<br>    starsGeometry.vertices.push(star);<br>&#125;<br>var starsMaterial = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">PointsMaterial(&#123;<span class="hljs-params">color</span>: 0x888888&#125;)</span>;<br>var starField = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">Points(<span class="hljs-params">starsGeometry</span>, <span class="hljs-params">starsMaterial</span>)</span>;<br>scene.add(starField);<br></code></pre></td></tr></table></figure><p>使用一个空的几何体，将自己创建的顶点坐标放入，也可以实现一组粒子的创建。如果我们需要单独设置每一个粒子的颜色，可以给 geometry 的 colors 数组添加相应数量的颜色：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs reasonml"><span class="hljs-keyword">for</span> (var i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10000</span>; i++) &#123;<br>    var star = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">Vector3()</span>;<br>    <span class="hljs-comment">//THREE.Math.randFloatSpread 在区间内随机浮动* - 范围 / 2 *到* 范围 / 2 *内随机取值。</span><br>    star.x = <span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">THREE</span>.</span><span class="hljs-module"><span class="hljs-identifier">Math</span>.</span></span>rand<span class="hljs-constructor">FloatSpread(2000)</span>;<br>    star.y = <span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">THREE</span>.</span><span class="hljs-module"><span class="hljs-identifier">Math</span>.</span></span>rand<span class="hljs-constructor">FloatSpread(2000)</span>;<br>    star.z = <span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">THREE</span>.</span><span class="hljs-module"><span class="hljs-identifier">Math</span>.</span></span>rand<span class="hljs-constructor">FloatSpread(2000)</span>;<br>    starsGeometry.vertices.push(star);<br><br>    starsGeometry.colors.push(<span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">Color(<span class="hljs-string">&quot;rgb(&quot;</span>+Math.<span class="hljs-params">random</span>()</span>*<span class="hljs-number">255</span>+<span class="hljs-string">&quot;, &quot;</span>+<span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">Math</span>.</span></span>random<span class="hljs-literal">()</span>*<span class="hljs-number">255</span>+<span class="hljs-string">&quot;, &quot;</span>+<span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">Math</span>.</span></span>random<span class="hljs-literal">()</span>*<span class="hljs-number">255</span>+<span class="hljs-string">&quot;)&quot;</span>)); <span class="hljs-comment">//添加一个随机颜色</span><br>&#125;<br></code></pre></td></tr></table></figure><h4 id="THREE-PointsMaterial-粒子的纹理"><a href="#THREE-PointsMaterial-粒子的纹理" class="headerlink" title="THREE.PointsMaterial 粒子的纹理"></a>THREE.PointsMaterial 粒子的纹理</h4><p>如果我们需要设置粒子的样式，还是需要通过设置 <code>THREE.PointsMaterial</code> 属性实现：</p><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs haxe"><span class="hljs-keyword">var</span> pointsMaterial = <span class="hljs-keyword">new</span> <span class="hljs-type">THREE</span>.PointsMaterial(&#123;color: <span class="hljs-type">0xff00ff</span>&#125;); <span class="hljs-comment">//设置了粒子纹理的颜色</span><br></code></pre></td></tr></table></figure><p>我们还可以通过 PointsMaterial 的 size 属性设置粒子的大小：</p><figure class="highlight processing"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs processing">var pointsMaterial = <span class="hljs-keyword">new</span> THREE.PointsMaterial(&#123;<span class="hljs-built_in">color</span>: <span class="hljs-number">0xff00ff</span>, <span class="hljs-built_in">size</span>:<span class="hljs-number">4</span>&#125;); <span class="hljs-comment">//粒子的尺寸改为原来的四倍</span><br><span class="hljs-comment">//或者直接设置属性</span><br>pointsMaterial.<span class="hljs-built_in">size</span> = <span class="hljs-number">4</span>;<br></code></pre></td></tr></table></figure><p>我们也可以给粒子设置纹理：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">var pointsMaterial = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">PointsMaterial(&#123;<span class="hljs-params">color</span>: 0xff00ff, <span class="hljs-params">map</span>:<span class="hljs-params">texture</span>&#125;)</span>; <span class="hljs-comment">//添加纹理</span><br></code></pre></td></tr></table></figure><p>默认粒子是不受光照影响的，我们可以设置 lights 属性为 true，让粒子受光照影响：</p><figure class="highlight processing"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs processing">var pointsMaterial = <span class="hljs-keyword">new</span> THREE.PointsMaterial(&#123;<span class="hljs-built_in">color</span>: <span class="hljs-number">0xff00ff</span>, <span class="hljs-built_in">lights</span>:<span class="hljs-keyword">true</span>&#125;); <br><span class="hljs-comment">//或者</span><br>pointsMaterial.<span class="hljs-built_in">lights</span> = <span class="hljs-keyword">true</span>; <span class="hljs-comment">//开启受光照影响</span><br></code></pre></td></tr></table></figure><p>我们也可以设置粒子不受到距离的影响产生近大远小的效果：</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">var pointsMaterial = <span class="hljs-keyword">new</span> THREE.<span class="hljs-constructor">PointsMaterial(&#123;<span class="hljs-params">color</span>: 0xff00ff, <span class="hljs-params">sizeAttenuation</span>: <span class="hljs-params">false</span>&#125;)</span>; <br><span class="hljs-comment">//或者</span><br>pointsMaterial.sizeAttenuation = <span class="hljs-literal">false</span>; <span class="hljs-comment">//关闭粒子的显示效果受距离影响</span><br></code></pre></td></tr></table></figure><p>粒子的效果就介绍到这里，希望大家熟练了以后能够做出来各种各样的花哨效果。</p><p>接下来展示下我给大家准备的粒子案例：<a href="https://johnson2heng.github.io/GitChat-Three.js/08第八节 points/points.html">点击这里</a>。</p><p>代码查看地址：<a href="https://github.com/johnson2heng/GitChat-Three.js/blob/master/08第八节 points/points.html">点击这里</a>。</p><p>这个案例和精灵的案例区别就是，将球体改成了粒子，然后将立方体修改成了带有 canvas 纹理的粒子，并且在背景里面添加了一万个粒子 </p>]]></content>
    
    
    
    <tags>
      
      <tag>Threejs</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>Threejs学习和总结前传</title>
    <link href="/blog/posts/456b6ccc.html"/>
    <url>/blog/posts/456b6ccc.html</url>
    
    <content type="html"><![CDATA[<h2 id="学习-Three-js-之前要知道的"><a href="#学习-Three-js-之前要知道的" class="headerlink" title="学习 Three.js 之前要知道的"></a>学习 Three.js 之前要知道的</h2><img src="/blog/posts/456b6ccc/QQ%E5%9B%BE%E7%89%8720190929102706.png" class=""><span id="more"></span><h3 id="什么是-WebGL？"><a href="#什么是-WebGL？" class="headerlink" title="什么是 WebGL？"></a>什么是 WebGL？</h3><p>WebGL（Web 图形库）是一种 JavaScript API，用于在任何兼容的 Web 浏览器中呈现交互式 3D 和 2D 图形，而无需使用插件。WebGL 通过引入一个与 OpenGL ES 2.0 紧密相符合的 API，可以在 HTML5 <code>&lt;canvas&gt;</code> 元素中使用。WebGL 给我们提供了一系列的图形接口，能够让我们通过 JavaScript 去使用 GPU 来进行浏览器图形渲染的工具。</p><h3 id="什么是-Three-js？"><a href="#什么是-Three-js？" class="headerlink" title="什么是 Three.js？"></a>什么是 Three.js？</h3><p>Three.js 是一款 webGL 框架，由于其易用性被广泛应用。Three.js 在 WebGL 的 API 接口基础上，又进行的一层封装。Three.js 以简单、直观的方式封装了 3D 图形编程中常用的对象。Three.js 在开发中使用了很多图形引擎的高级技巧，极大地提高了性能。另外，由于内置了很多常用对象和极易上手的工具，Three.js 的功能也非常强大，Three.js 还是完全开源的。</p><h4 id="WEBGL-和-Three-js-的关系"><a href="#WEBGL-和-Three-js-的关系" class="headerlink" title="WEBGL 和 Three.js 的关系"></a>WEBGL 和 Three.js 的关系</h4><p>WebGL 原生 API 是一种非常低级的接口，而且还需要一些数学和图形学的相关技术。对于没有相关基础的人来说，入门真的很难，Three.js 将入门的门槛降低了一大截，对 WebGL 进行封装，简化我们创建三维动画场景的过程。</p><p>用最简单的一句话概括：WebGL 和 Three.js 的关系，相当于 JavaScript 和 jQuery 的关系。</p><h4 id="功能概述"><a href="#功能概述" class="headerlink" title="功能概述"></a>功能概述</h4><p>Three.js 作为 WebGL 框架中的佼佼者，由于它的易用性和扩展性，使得它能够满足大部分的开发需求，Three.js 的具体功能如下：</p><ol><li><strong>Three.js 掩盖了 3D 渲染的细节</strong>：Three.js 将 WebGL 原生 API 的细节抽象化，将 3D 场景拆解为网格、材质和光源（即它内置了图形编程常用的一些对象种类）。</li><li><strong>面向对象</strong>：开发者可以使用上层的 JavaScript 对象，而不是仅仅调用 JavaScript 函数。</li><li><strong>功能非常丰富</strong>：Three.js 除封装了 WebGL 原始 API 之外，Three.js 还包含了许多实用的内置对象，可以方便地应用于游戏开发、动画制作、幻灯片制作、髙分辨率模型和一些特殊的视觉效果制作。</li><li><strong>速度很快</strong>：Three.js 采用了 3D 图形最佳实践来保证在不失可用性的前提下，保持极高的性能。</li><li><strong>支持交互</strong>：WebGL 本身并不提供拾取（Picking）功能（即是否知道鼠标正处于某个物体上）。而 Three.js 则固化了拾取支持，这就使得你可以轻松为你的应用添加交互功能。</li><li><strong>包含数学库</strong>：Three.js 拥有一个强大易用的数学库，你可以在其中进行矩阵、投影和矢量运算。</li><li><strong>内置文件格式支持</strong>：你可以使用流行的 3D 建模软件导出文本格式的文件，然后使用 Three.js 加载，也可以使用 Three.js 自己的 JSON 格式或二进制格式。</li><li><strong>扩展性很强</strong>：为 Three.js 添加新的特性或进行自定义优化是很容易的事情。如果你需要某个特殊的数据结构，那么只需要封装到 Three.js 即可。</li><li><strong>支持 HTML5 Canvas</strong>：Three.js 不但支持 WebGL，而且还支持使用 Canvas2D、Css3D 和 SVG 进行渲染。在未兼容 WebGL 的环境中可以回退到其它的解决方案。</li></ol><h4 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h4><ul><li>官网文档非常粗糙，对于新手极度不友好。</li><li>国内的相关资源匮乏。</li><li>Three.js 所有的资料都是以英文格式存在，对国内的朋友来说又提高了门槛。</li><li>Three.js 不是游戏引擎，一些游戏相关的功能没有封装在里面，如果需要相关的功能需要进行二次开发。</li></ul><h4 id="Three-js-与其他库的对比"><a href="#Three-js-与其他库的对比" class="headerlink" title="Three.js 与其他库的对比"></a>Three.js 与其他库的对比</h4><div class="table-container"><table><thead><tr><th>库名</th><th>描述</th><th>相比 Three.js</th></tr></thead><tbody><tr><td>Babylon.js</td><td>是最好的 JavaScript 3D 游戏引擎，它能创建专业级三维游戏。主要以游戏开发和易用性为主</td><td><p> Three.js 比较全面，而 Babylon.js 专注于游戏方面。 </p> <p>Babylon.js 提供了对碰撞检测、场景重力、面向游戏的照相机，Three.js 本身不自带，需要依靠引入插件实现。</p><p> 对于 WebGL 的封装，双方做得各有千秋，Three.js 浅一些，好处是易于扩展，易于向更底层学习；Babylon.js 深一些，好处是易用，但扩展难度大一些。 </p><p> Three.js 的发展依靠社区推动，出来的比较早，发展比较成熟，Babylon.js 由微软公司在 2013 推出，文档和社区都比较健全，国内还不怎么火。</p></td></tr><tr><td>PlayCanvas</td><td>是一个基于 WebGL 游戏引擎的企业级开源 JavaScript 框架，它有许多的开发工具能帮你快速创建 3D 游戏</td><td>PlayCanvas 的优势在于它有云端的在线可视化编辑工具。 PlayCanvas 的扩展性不如 Three.js。 最主要是 PlayCanvas 不完全开源，还商业付费。</td></tr><tr><td>Cesium</td><td>是国外一个基于 JavaScript 编写的使用 WebGL 的地图引擎，支持 3D、2D、2.5D 形式的地图展示，可以自行绘制图形，高亮区域。</td><td>Cesium 是一个地图引擎，专注于 GIS，相关项目推荐使用它，其它项目还是算了。 至于库的扩展，其它的配套插件，以及周边的资源都不及 Three.js。</td></tr></tbody></table></div><p>Three.js 在其库的扩展性，易用性以及功能方面有很好的优势。</p><h3 id="Three-js-master-源码目录结构"><a href="#Three-js-master-源码目录结构" class="headerlink" title="Three.js-master 源码目录结构"></a>Three.js-master 源码目录结构</h3><ul><li>build：里面含有 Three.js 构建出来的 JavaScript 文件，可以直接引入使用，并有压缩版；</li><li>docs：Three.js 的官方文档；</li><li>editor：Three.js 的一个网页版的模型编辑器；</li><li>examples：Three.js 的官方案例，如果全都学会，必将成为大神；</li><li>src：这里面放置的全是编译 Three.js 的源文件；</li><li>test：一些官方测试代码，我们一般用不到；</li><li>utils：一些相关插件；</li><li>其他：开发环境搭建、开发所需要的文件，如果不对 Three.js 进行二次开发，用不到。</li></ul><h3 id="使用-Three-js"><a href="#使用-Three-js" class="headerlink" title="使用 Three.js"></a>使用 Three.js</h3><h4 id="第一个案例"><a href="#第一个案例" class="headerlink" title="第一个案例"></a>第一个案例</h4><p><div class="note info">所有关于Threejs的代码均在https://github.com/Cenergy/webpack-threejs.git</div><br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">&quot;utf-8&quot;</span> /&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>第一个Three.js案例<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css"></span><br><span class="css">      <span class="hljs-selector-tag">body</span> &#123;</span><br><span class="css">        <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;</span><br><span class="css">      &#125;</span><br><span class="css"></span><br><span class="css">      <span class="hljs-selector-tag">canvas</span> &#123;</span><br><span class="css">        <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;</span><br><span class="css">        <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;</span><br><span class="css">        <span class="hljs-attribute">display</span>: block;</span><br><span class="css">      &#125;</span><br><span class="css">    </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span><br><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">onload</span>=<span class="hljs-string">&quot;init()&quot;</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;https://cdn.bootcss.com/three.js/108/three.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript"></span><br><span class="javascript">      <span class="hljs-comment">//声明一些全局变量</span></span><br><span class="javascript">      <span class="hljs-keyword">var</span> renderer, camera, scene, geometry, material, mesh;</span><br><span class="javascript"></span><br><span class="javascript">      <span class="hljs-comment">//初始化渲染器</span></span><br><span class="javascript">      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initRenderer</span>(<span class="hljs-params"></span>) </span>&#123;</span><br><span class="javascript">        renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer(); <span class="hljs-comment">//实例化渲染器</span></span><br><span class="javascript">        renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight); <span class="hljs-comment">//设置宽和高</span></span><br><span class="javascript">        <span class="hljs-built_in">document</span>.body.appendChild(renderer.domElement); <span class="hljs-comment">//添加到dom</span></span><br><span class="javascript">      &#125;</span><br><span class="javascript"></span><br><span class="javascript">      <span class="hljs-comment">//初始化场景</span></span><br><span class="javascript">      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initScene</span>(<span class="hljs-params"></span>) </span>&#123;</span><br><span class="javascript">        scene = <span class="hljs-keyword">new</span> THREE.Scene(); <span class="hljs-comment">//实例化场景</span></span><br><span class="javascript">      &#125;</span><br><span class="javascript"></span><br><span class="javascript">      <span class="hljs-comment">//初始化相机</span></span><br><span class="javascript">      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initCamera</span>(<span class="hljs-params"></span>) </span>&#123;</span><br><span class="javascript">        camera = <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(</span><br><span class="javascript">          <span class="hljs-number">45</span>,</span><br><span class="javascript">          <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight,</span><br><span class="javascript">          <span class="hljs-number">0.1</span>,</span><br><span class="javascript">          <span class="hljs-number">200</span></span><br><span class="javascript">        ); <span class="hljs-comment">//实例化相机</span></span><br><span class="javascript">        camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">15</span>);</span><br><span class="javascript">      &#125;</span><br><span class="javascript"></span><br><span class="javascript">      <span class="hljs-comment">//创建模型</span></span><br><span class="javascript">      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initMesh</span>(<span class="hljs-params"></span>) </span>&#123;</span><br><span class="javascript">        geometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>); <span class="hljs-comment">//创建几何体</span></span><br><span class="javascript">        material = <span class="hljs-keyword">new</span> THREE.MeshNormalMaterial(); <span class="hljs-comment">//创建材质</span></span><br><span class="javascript">        mesh = <span class="hljs-keyword">new</span> THREE.Mesh(geometry, material); <span class="hljs-comment">//创建网格</span></span><br><span class="javascript">        scene.add(mesh); <span class="hljs-comment">//将网格添加到场景</span></span><br><span class="javascript">      &#125;</span><br><span class="javascript"></span><br><span class="javascript">      <span class="hljs-comment">//运行动画</span></span><br><span class="javascript">      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>&#123;</span><br><span class="javascript">        requestAnimationFrame(animate); <span class="hljs-comment">//循环调用函数</span></span><br><span class="javascript">        mesh.rotation.x += <span class="hljs-number">0.01</span>; <span class="hljs-comment">//每帧网格模型的沿x轴旋转0.01弧度</span></span><br><span class="javascript">        mesh.rotation.y += <span class="hljs-number">0.02</span>; <span class="hljs-comment">//每帧网格模型的沿y轴旋转0.02弧度</span></span><br><span class="javascript">        renderer.render(scene, camera); <span class="hljs-comment">//渲染界面</span></span><br><span class="javascript">      &#125;</span><br><span class="javascript"></span><br><span class="javascript">      <span class="hljs-comment">//初始化函数，页面加载完成是调用</span></span><br><span class="javascript">      <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">init</span>(<span class="hljs-params"></span>) </span>&#123;</span><br><span class="javascript">        initRenderer();</span><br><span class="javascript">        initScene();</span><br><span class="javascript">        initCamera();</span><br><span class="javascript">        initMesh();</span><br><span class="javascript">        animate();</span><br><span class="javascript">      &#125;</span><br><span class="javascript">    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span><br></code></pre></td></tr></table></figure></p>]]></content>
    
    
    
    <tags>
      
      <tag>Threejs</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>webpack学习与总结</title>
    <link href="/blog/posts/6e7939e6.html"/>
    <url>/blog/posts/6e7939e6.html</url>
    
    <content type="html"><![CDATA[<img src="/blog/posts/6e7939e6/webpack.png" class=""><span id="more"></span><h2 id="初识-Webpack"><a href="#初识-Webpack" class="headerlink" title="初识 Webpack"></a>初识 Webpack</h2><p>webpack 是模块打包工具，把源代码转换成发布到线上的可执行 JavaScrip、CSS、HTML 代码，包括如下内容。</p><ul><li>代码转换：TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等。</li><li>文件优化：压缩 JavaScript、CSS、HTML 代码，压缩合并图片等。</li><li>代码分割：提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载。</li><li>模块合并：在采用模块化的项目里会有很多个模块和文件，需要构建功能把模块分类合并成一个文件。</li><li>自动刷新：监听本地源代码的变化，自动重新构建、刷新浏览器。</li><li>代码校验：在代码被提交到仓库前需要校验代码是否符合规范，以及单元测试是否通过。</li><li>自动发布：更新完代码后，自动构建出线上发布代码并传输给发布系统。</li></ul><p>构建其实是工程化、自动化思想在前端开发中的体现，把一系列流程用代码去实现，让代码自动化地执行这一系列复杂的流程。 构建给前端开发注入了更大的活力，解放了我们的生产力。</p><p>历史上先后出现一系列构建工具，它们各有其优缺点。由于前端工程师很熟悉 JavaScript ，Node.js 又可以胜任所有构建需求，所以大多数构建工具都是用 Node.js 开发的。下面来一一介绍它们。</p><h3 id="Npm-Script"><a href="#Npm-Script" class="headerlink" title="Npm Script"></a>Npm Script</h3><p><a href="https://docs.npmjs.com/misc/scripts">Npm Script</a> 是一个任务执行者。Npm 是在安装 Node.js 时附带的包管理器，Npm Script 则是 Npm 内置的一个功能，允许在 <code>package.json</code> 文件里面使用 <code>scripts</code> 字段定义任务：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs json">&#123;<br>  <span class="hljs-attr">&quot;scripts&quot;</span>: &#123;<br>    <span class="hljs-attr">&quot;dev&quot;</span>: <span class="hljs-string">&quot;node dev.js&quot;</span>,<br>    <span class="hljs-attr">&quot;pub&quot;</span>: <span class="hljs-string">&quot;node build.js&quot;</span><br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>里面的 <code>scripts</code> 字段是一个对象，每个属性对应一段 Shell 脚本，以上代码定义了两个任务 <code>dev</code> 和 <code>pub</code>。 其底层实现原理是通过调用 Shell 去运行脚本命令，例如执行 <code>npm run pub</code> 命令等同于执行命令 <code>node build.js</code>。</p><p>Npm Script的优点是内置，无须安装其他依赖。其缺点是功能太简单，虽然提供了 <code>pre</code> 和 <code>post</code> 两个钩子，但不能方便地管理多个任务之间的依赖。</p><h3 id="Grunt"><a href="#Grunt" class="headerlink" title="Grunt"></a>Grunt</h3><p><a href="https://gruntjs.com/">Grunt</a> 和 Npm Script 类似，也是一个任务执行者。Grunt 有大量现成的插件封装了常见的任务，也能管理任务之间的依赖关系，自动化执行依赖的任务，每个任务的具体执行代码和依赖关系写在配置文件 <code>Gruntfile.js</code> 里，例如：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">grunt</span>) </span>&#123;<br>  <span class="hljs-comment">// 所有插件的配置信息</span><br>  grunt.initConfig(&#123;<br>    <span class="hljs-comment">// uglify 插件的配置信息</span><br>    <span class="hljs-attr">uglify</span>: &#123;<br>      <span class="hljs-attr">app_task</span>: &#123;<br>        <span class="hljs-attr">files</span>: &#123;<br>          <span class="hljs-string">&#x27;build/app.min.js&#x27;</span>: [<span class="hljs-string">&#x27;lib/index.js&#x27;</span>, <span class="hljs-string">&#x27;lib/test.js&#x27;</span>]<br>        &#125;<br>      &#125;<br>    &#125;,<br>    <span class="hljs-comment">// watch 插件的配置信息</span><br>    <span class="hljs-attr">watch</span>: &#123;<br>      <span class="hljs-attr">another</span>: &#123;<br>          <span class="hljs-attr">files</span>: [<span class="hljs-string">&#x27;lib/*.js&#x27;</span>],<br>      &#125;<br>    &#125;<br>  &#125;);<br><br>  <span class="hljs-comment">// 告诉 grunt 我们将使用这些插件</span><br>  grunt.loadNpmTasks(<span class="hljs-string">&#x27;grunt-contrib-uglify&#x27;</span>);<br>  grunt.loadNpmTasks(<span class="hljs-string">&#x27;grunt-contrib-watch&#x27;</span>);<br><br>  <span class="hljs-comment">// 告诉grunt当我们在终端中启动 grunt 时需要执行哪些任务</span><br>  grunt.registerTask(<span class="hljs-string">&#x27;dev&#x27;</span>, [<span class="hljs-string">&#x27;uglify&#x27;</span>,<span class="hljs-string">&#x27;watch&#x27;</span>]);<br>&#125;;<br></code></pre></td></tr></table></figure><p>在项目根目录下执行命令 <code>grunt dev</code> 就会启动 JavaScript 文件压缩和自动刷新功能。</p><p>Grunt的优点是：</p><ul><li>灵活，它只负责执行你定义的任务；</li><li>大量的可复用插件封装好了常见的构建任务。</li></ul><p>Grunt的缺点是集成度不高，要写很多配置后才可以用，无法做到开箱即用。</p><p>Grunt 相当于进化版的 Npm Script，它的诞生其实是为了弥补 Npm Script 的不足。</p><h3 id="Gulp"><a href="#Gulp" class="headerlink" title="Gulp"></a>Gulp</h3><p><a href="http://gulpjs.com/">Gulp</a> 是一个基于流的自动化构建工具。 除了可以管理和执行任务，还支持监听文件、读写文件。Gulp 被设计得非常简单，只通过下面5个方法就可以胜任几乎所有构建场景：</p><ul><li>通过 <code>gulp.task</code> 注册一个任务；</li><li>通过 <code>gulp.run</code> 执行任务；</li><li>通过 <code>gulp.watch</code> 监听文件变化；</li><li>通过 <code>gulp.src</code> 读取文件；</li><li>通过 <code>gulp.dest</code> 写文件。</li></ul><p>Gulp 的最大特点是引入了流的概念，同时提供了一系列常用的插件去处理流，流可以在插件之间传递，大致使用如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// 引入 Gulp</span><br><span class="hljs-keyword">var</span> gulp = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;gulp&#x27;</span>); <br><span class="hljs-comment">// 引入插件</span><br><span class="hljs-keyword">var</span> jshint = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;gulp-jshint&#x27;</span>);<br><span class="hljs-keyword">var</span> sass = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;gulp-sass&#x27;</span>);<br><span class="hljs-keyword">var</span> concat = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;gulp-concat&#x27;</span>);<br><span class="hljs-keyword">var</span> uglify = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;gulp-uglify&#x27;</span>);<br><br><span class="hljs-comment">// 编译 SCSS 任务</span><br>gulp.task(<span class="hljs-string">&#x27;sass&#x27;</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;<br>  <span class="hljs-comment">// 读取文件通过管道喂给插件</span><br>  gulp.src(<span class="hljs-string">&#x27;./scss/*.scss&#x27;</span>)<br>    <span class="hljs-comment">// SCSS 插件把 scss 文件编译成 CSS 文件</span><br>    .pipe(sass())<br>    <span class="hljs-comment">// 输出文件</span><br>    .pipe(gulp.dest(<span class="hljs-string">&#x27;./css&#x27;</span>));<br>&#125;);<br><br><span class="hljs-comment">// 合并压缩 JS</span><br>gulp.task(<span class="hljs-string">&#x27;scripts&#x27;</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;<br>  gulp.src(<span class="hljs-string">&#x27;./js/*.js&#x27;</span>)<br>    .pipe(concat(<span class="hljs-string">&#x27;all.js&#x27;</span>))<br>    .pipe(uglify())<br>    .pipe(gulp.dest(<span class="hljs-string">&#x27;./dist&#x27;</span>));<br>&#125;);<br><br><span class="hljs-comment">// 监听文件变化</span><br>gulp.task(<span class="hljs-string">&#x27;watch&#x27;</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>&#123;<br>  <span class="hljs-comment">// 当 scss 文件被编辑时执行 SCSS 任务</span><br>  gulp.watch(<span class="hljs-string">&#x27;./scss/*.scss&#x27;</span>, [<span class="hljs-string">&#x27;sass&#x27;</span>]);<br>  gulp.watch(<span class="hljs-string">&#x27;./js/*.js&#x27;</span>, [<span class="hljs-string">&#x27;scripts&#x27;</span>]);    <br>&#125;);<br></code></pre></td></tr></table></figure><p>Gulp 的优点是好用又不失灵活，既可以单独完成构建也可以和其它工具搭配使用。其缺点是和 Grunt 类似，集成度不高，要写很多配置后才可以用，无法做到开箱即用。</p><p>可以将Gulp 看作 Grunt 的加强版。相对于 Grunt，Gulp增加了监听文件、读写文件、流式处理的功能。</p><h3 id="Webpack"><a href="#Webpack" class="headerlink" title="Webpack"></a>Webpack</h3><p><a href="https://webpack.js.org/">Webpack</a> 是一个打包模块化 JavaScript 的工具，在 Webpack 里一切文件皆模块，通过 Loader 转换文件，通过 Plugin 注入钩子，最后输出由多个模块组合成的文件。Webpack 专注于构建模块化项目。</p><p>其官网的首页图很形象的画出了 Webpack 是什么，如下：</p><img src="/blog/posts/6e7939e6/1569474242621.png" class="" width="1569474242621"><p>一切文件：JavaScript、CSS、SCSS、图片、模板，在 Webpack 眼中都是一个个模块，这样的好处是能清晰的描述出各个模块之间的依赖关系，以方便 Webpack 对模块进行组合和打包。 经过 Webpack 的处理，最终会输出浏览器能使用的静态资源。</p><p>Webpack 具有很大的灵活性，能配置如何处理文件，大致使用如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-built_in">module</span>.exports = &#123;<br>  <span class="hljs-comment">// 所有模块的入口，Webpack 从入口开始递归解析出所有依赖的模块</span><br>  <span class="hljs-attr">entry</span>: <span class="hljs-string">&#x27;./app.js&#x27;</span>,<br>  <span class="hljs-attr">output</span>: &#123;<br>    <span class="hljs-comment">// 把入口所依赖的所有模块打包成一个文件 bundle.js 输出 </span><br>    <span class="hljs-attr">filename</span>: <span class="hljs-string">&#x27;bundle.js&#x27;</span><br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>Webpack的优点是：</p><ul><li>专注于处理模块化的项目，能做到开箱即用一步到位；</li><li>通过 Plugin 扩展，完整好用又不失灵活；</li><li>使用场景不仅限于 Web 开发；</li><li>社区庞大活跃，经常引入紧跟时代发展的新特性，能为大多数场景找到已有的开源扩展；</li><li>良好的开发体验。</li></ul><p>Webpack的缺点是只能用于采用模块化开发的项目。</p><h3 id="为什么选择-Webpack"><a href="#为什么选择-Webpack" class="headerlink" title="为什么选择 Webpack"></a>为什么选择 Webpack</h3><p>上面介绍的构建工具是按照它们诞生的时间排序的，它们是时代的产物，侧面反映出 Web 开发的发展趋势如下：</p><ol><li>在 Npm Script 和 Grunt 时代，Web 开发要做的事情变多，流程复杂，自动化思想被引入，用于简化流程；</li><li>在 Gulp 时代开始出现一些新语言用于提高开发效率，流式处理思想的出现是为了简化文件转换的流程，例如将 ES6 转换成 ES5。</li><li>在 Webpack 时代由于单页应用的流行，一个网页的功能和实现代码变得庞大，Web 开发向模块化改进。</li></ol><p>这些构建工具都有各自的定位和专注点，它们之间既可以单独地完成任务，也可以相互搭配起来弥补各自的不足。 在了解这些常见的构建工具后，你需要根据自己的需求去判断应该如何选择和搭配它们才能更好地完成自己的需求。</p><p>经过多年的发展， Webpack 已经成为构建工具中的首选，这是有原因的：</p><ul><li>大多数团队在开发新项目时会采用紧跟时代的技术，这些技术几乎都会采用“模块化+新语言+新框架”，Webpack 可以为这些新项目提供一站式的解决方案；</li><li>Webpack 有良好的生态链和维护团队，能提供良好的开发体验和保证质量；</li><li>Webpack 被全世界的大量 Web 开发者使用和验证，能找到各个层面所需的教程和经验分享。</li></ul><h2 id="使用Webpack"><a href="#使用Webpack" class="headerlink" title="使用Webpack"></a>使用Webpack</h2><h3 id="安装-Webpack-到本项目"><a href="#安装-Webpack-到本项目" class="headerlink" title="安装 Webpack 到本项目"></a>安装 Webpack 到本项目</h3><p>在开始给项目加入构建前， 在安装 Webpack 前请确保你的系统安装了5.0.0及以上版本的 <a href="https://nodejs.org/">Node.js</a>。你还需要先新建一个 Web 项目，进入项目根目录执行 <code>npm init</code> 来初始化最简单的采用了模块化开发的项目；</p><p>要安装 Webpack 到本项目，可按照你的需要选择以下任意命令运行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># npm i -D 是 npm install --save-dev 的简写，是指安装模块并保存到 package.json 的 devDependencies</span><br><span class="hljs-comment"># 安装最新稳定版</span><br>npm i -D webpack<br><br><span class="hljs-comment"># 安装指定版本</span><br>npm i -D webpack@&lt;version&gt;<br><br><span class="hljs-comment"># 安装最新体验版本</span><br>npm i -D webpack@beta<br></code></pre></td></tr></table></figure><p>安装完后你可以通过这些途径运行安装到本项目的 Webpack：</p><ul><li><p>在项目根目录下对应的命令行里通过 <code>node_modules/.bin/webpack</code> 运行 Webpack 可执行文件。</p></li><li><p>在 <a href="http://webpack.wuhaolin.cn/1入门/常见的构建工具及对比/npm_script.md">Npm Script</a> 里定义的任务会优先使用本项目下的 Webpack，代码如下：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-string">&quot;scripts&quot;</span>: &#123;<br>    <span class="hljs-attr">&quot;start&quot;</span>: <span class="hljs-string">&quot;webpack --config webpack.config.js&quot;</span><br>&#125;<br></code></pre></td></tr></table></figure></li><li><p>可以使用<code>npx webpack</code></p><p>Node 自带 npm 模块，所以可以直接使用 npx 命令。万一不能用，就要手动安装一下。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">npm install -g npx<br></code></pre></td></tr></table></figure><p>npx 想要解决的主要问题，就是调用项目内部安装的模块。比如，项目内部安装了 webpack 而且全局没有安装 webpack。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">npm install -D webpack<br></code></pre></td></tr></table></figure><p>一般来说，调用 webpack，只能在项目脚本和 package.json 的<a href="http://www.ruanyifeng.com/blog/2016/10/npm_scripts.html"><code>scripts</code></a>字段里面， 如果想在命令行下调用，必须像下面这样。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 项目的根目录下执行</span><br><span class="hljs-built_in">cd</span> node_modules\.bin<br>webpack --version   <span class="hljs-comment"># 输出 4.41.0</span><br></code></pre></td></tr></table></figure><p>npx 就是想解决这个问题，让项目内部安装的模块用起来更方便，只要像下面这样调用就行了。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 项目的根目录下执行</span><br>npx webpack --version<br></code></pre></td></tr></table></figure><p>npx 的原理很简单，就是运行的时候，会到<code>node_modules/.bin</code>路径和环境变量<code>$PATH</code>里面，检查命令是否存在。</p><p>由于 npx 会检查环境变量<code>$PATH</code>，所以系统命令也可以调用。</p><p>除了调用项目内部模块，npx 还能避免全局安装的模块。比如，<code>create-react-app</code>这个模块是全局安装，npx 可以运行它，而且不进行全局安装。</p><blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ npx create-react-app my-react-app<br></code></pre></td></tr></table></figure></blockquote><p>上面代码运行时，npx 将<code>create-react-app</code>下载到一个临时目录，使用以后再删除。所以，以后再次执行上面的命令，会重新下载<code>create-react-app</code>。</p><p>下载全局模块时，npx 允许指定版本。</p><blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ npx uglify-js@3.1.0 main.js -o ./dist/main.js<br></code></pre></td></tr></table></figure></blockquote><p>上面代码指定使用 3.1.0 版本的<code>uglify-js</code>压缩脚本。</p><p>注意，只要 npx 后面的模块无法在本地发现，就会下载同名模块。比如，本地没有安装<code>http-server</code>模块，下面的命令会自动下载该模块，在当前目录启动一个 Web 服务。</p><blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ npx http-server<br></code></pre></td></tr></table></figure><p>参考：<a href="http://www.ruanyifeng.com/blog/2019/02/npx.html">npx 使用教程</a></p></blockquote></li></ul><h3 id="安装-Webpack-到全局"><a href="#安装-Webpack-到全局" class="headerlink" title="安装 Webpack 到全局"></a>安装 Webpack 到全局</h3><p>安装到全局后你可以在任何地方共用一个 Webpack 可执行文件，而不用各个项目重复安装，安装方式如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">npm i -g webpack<br></code></pre></td></tr></table></figure><p><div class="note danger">虽然介绍了以上两种安装方式，但是我们推荐安装到本项目，原因是可防止不同项目依赖不同版本的 Webpack 而导致冲突。</div></p><h3 id="使用-Webpack"><a href="#使用-Webpack" class="headerlink" title="使用 Webpack"></a>使用 Webpack</h3><h4 id="默认配置"><a href="#默认配置" class="headerlink" title="默认配置"></a>默认配置</h4><p>Webpack 在执行构建时默认会从项目根目录下的 <code>webpack.config.js</code> 文件读取配置，所以你还需要新建它，其内容如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;path&#x27;</span>);<br><br><span class="hljs-built_in">module</span>.exports = &#123;<br>  <span class="hljs-comment">// JavaScript 执行入口文件</span><br>  <span class="hljs-attr">entry</span>: <span class="hljs-string">&#x27;./main.js&#x27;</span>,<br>  <span class="hljs-attr">output</span>: &#123;<br>    <span class="hljs-comment">// 把所有依赖的模块合并输出到一个 bundle.js 文件</span><br>    <span class="hljs-attr">filename</span>: <span class="hljs-string">&#x27;bundle.js&#x27;</span>,<br>    <span class="hljs-comment">// 输出文件都放到 dist 目录下</span><br>    <span class="hljs-attr">path</span>: path.resolve(__dirname, <span class="hljs-string">&#x27;./dist&#x27;</span>),<br>  &#125;<br>&#125;;<br></code></pre></td></tr></table></figure><p>由于 Webpack 构建运行在 Node.js 环境下，所以该文件最后需要通过 CommonJS 规范导出一个描述如何构建的 <code>Object</code> 对象。</p><h4 id="使用loader"><a href="#使用loader" class="headerlink" title="使用loader"></a>使用loader</h4><p>Webpack 把一切文件看作模块，CSS 文件也不例外，Webpack 不原生支持解析 CSS 文件。要支持非 JavaScript 类型的文件，需要使用 Webpack 的 Loader 机制。Webpack的配置修改使用如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;path&#x27;</span>);<br><br><span class="hljs-built_in">module</span>.exports = &#123;<br>  <span class="hljs-comment">// JavaScript 执行入口文件</span><br>  <span class="hljs-attr">entry</span>: <span class="hljs-string">&#x27;./main.js&#x27;</span>,<br>  <span class="hljs-attr">output</span>: &#123;<br>    <span class="hljs-comment">// 把所有依赖的模块合并输出到一个 bundle.js 文件</span><br>    <span class="hljs-attr">filename</span>: <span class="hljs-string">&#x27;bundle.js&#x27;</span>,<br>    <span class="hljs-comment">// 输出文件都放到 dist 目录下</span><br>    <span class="hljs-attr">path</span>: path.resolve(__dirname, <span class="hljs-string">&#x27;./dist&#x27;</span>),<br>  &#125;,<br>  <span class="hljs-attr">module</span>: &#123;<br>    <span class="hljs-attr">rules</span>: [<br>      &#123;<br>        <span class="hljs-comment">// 用正则去匹配要用该 loader 转换的 CSS 文件</span><br>        <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.css$/</span>,<br>        use: [<span class="hljs-string">&#x27;style-loader&#x27;</span>, <span class="hljs-string">&#x27;css-loader?minimize&#x27;</span>],<br>      &#125;<br>    ]<br>  &#125;<br>&#125;;<br></code></pre></td></tr></table></figure><p>Loader 可以看作具有文件转换功能的翻译员，配置里的 <code>module.rules</code> 数组配置了一组规则，告诉 Webpack 在遇到哪些文件时使用哪些 Loader 去加载和转换。 如上配置告诉 Webpack 在遇到以 <code>.css</code> 结尾的文件时先使用 <code>css-loader</code> 读取 CSS 文件，再交给 <code>style-loader</code> 把 CSS 内容注入到 JavaScript 里。 在配置 Loader 时需要注意的是：</p><ul><li><code>use</code> 属性的值需要是一个由 Loader 名称组成的数组，Loader 的执行顺序是由后到前的；</li><li>每一个 Loader 都可以通过 URL querystring 的方式传入参数，例如 <code>css-loader?minimize</code> 中的 <code>minimize</code> 告诉 <code>css-loader</code> 要开启 CSS 压缩。</li></ul><p>Webpack 构建前要先安装新引入的 Loader：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">npm i -D style-loader css-loader<br></code></pre></td></tr></table></figure><p>给 Loader 传入属性的方式除了有 querystring 外，还可以通过 Object 传入，以上的 Loader 配置可以修改为如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs js">use: [<br>  <span class="hljs-string">&#x27;style-loader&#x27;</span>, <br>  &#123;<br>    <span class="hljs-attr">loader</span>:<span class="hljs-string">&#x27;css-loader&#x27;</span>,<br>    <span class="hljs-attr">options</span>:&#123;<br>      <span class="hljs-attr">minimize</span>:<span class="hljs-literal">true</span>,<br>    &#125;<br>  &#125;<br>]<br></code></pre></td></tr></table></figure><p>除了在 <code>webpack.config.js</code> 配置文件中配置 Loader 外，还可以在源码中指定用什么 Loader 去处理文件。 以加载 CSS 文件为例，修改上面例子中的 <code>main.js</code> 如下：</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;style-loader!css-loader?minimize!./main.css&#x27;</span>);<br></code></pre></td></tr></table></figure><p>这样就能指定对 <code>./main.css</code> 这个文件先采用 css-loader 再采用 style-loader 转换。</p><h4 id="使用-Plugin"><a href="#使用-Plugin" class="headerlink" title="使用 Plugin"></a>使用 Plugin</h4><p>plugin 可以在 webpack 运行到某一时刻的时候，帮你做一些事情。</p><p>htmlWepackPluginnn 会在打包结束后，自动生成一个 html 文件，并把打包的生成的 js 自动引入到这个 html 文件中。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> HtmlWebpackPlugin = <span class="hljs-built_in">require</span>(<span class="hljs-string">&quot;html-webpack-plugin&quot;</span>); <span class="hljs-comment">//通过 npm 安装</span><br><span class="hljs-keyword">const</span> webpack = <span class="hljs-built_in">require</span>(<span class="hljs-string">&quot;webpack&quot;</span>); <span class="hljs-comment">//访问内置的插件</span><br><span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">&quot;path&quot;</span>);<br><br><span class="hljs-keyword">const</span> config = &#123;<br>  <span class="hljs-attr">mode</span>: <span class="hljs-string">&quot;production&quot;</span>,<br>  <span class="hljs-attr">entry</span>: <span class="hljs-string">&quot;./src/index.js&quot;</span>,<br>  <span class="hljs-attr">output</span>: &#123;<br>    <span class="hljs-attr">filename</span>: <span class="hljs-string">&quot;bundle.js&quot;</span>,<br>    <span class="hljs-attr">path</span>: path.resolve(__dirname, <span class="hljs-string">&quot;dist&quot;</span>)<br>  &#125;,<br>  <span class="hljs-attr">module</span>: &#123;<br>    <span class="hljs-attr">rules</span>: [<br>      &#123;<br>        <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.(png|jpg|gif)$/</span>,<br>        use: <span class="hljs-string">&quot;file-loader&quot;</span><br>      &#125;<br>    ]<br>  &#125;,<br>  <span class="hljs-attr">plugins</span>: [<span class="hljs-keyword">new</span> HtmlWebpackPlugin(&#123; <span class="hljs-attr">template</span>: <span class="hljs-string">&quot;./src/index.html&quot;</span> &#125;)]<br>&#125;;<br><br><span class="hljs-built_in">module</span>.exports = config;<br></code></pre></td></tr></table></figure><h4 id="使用-DevServer"><a href="#使用-DevServer" class="headerlink" title="使用 DevServer"></a>使用 DevServer</h4><p>DevServer 会启动一个 HTTP 服务器用于服务网页请求，同时会帮助启动 Webpack ，并接收 Webpack 发出的文件更变信号，通过 WebSocket 协议自动刷新网页做到实时预览。</p><p>首先需要安装 DevServer：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">npm i -D webpack-dev-server<br></code></pre></td></tr></table></figure><p>安装成功后执行 <code>webpack-dev-server</code> 命令， DevServer 就启动了，这时你会看到控制台有一串日志输出：</p><figure class="highlight angelscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs angelscript">Project <span class="hljs-keyword">is</span> running at http:<span class="hljs-comment">//localhost:8080/</span><br>webpack output <span class="hljs-keyword">is</span> served <span class="hljs-keyword">from</span> /<br></code></pre></td></tr></table></figure><p>这意味着 DevServer 启动的 HTTP 服务器监听在 <code>http://localhost:8080/</code> ，DevServer 启动后会一直驻留在后台保持运行，访问这个网址你就能获取项目根目录下的 <code>index.html</code>。 用浏览器打开这个地址你会发现页面空白错误原因是 <code>./dist/bundle.js</code> 加载404了。 同时你会发现并没有文件输出到 <code>dist</code> 目录，原因是 DevServer 会把 Webpack 构建出的文件保存在内存中，在要访问输出的文件时，必须通过 HTTP 服务访问。 由于 DevServer 不会理会 <code>webpack.config.js</code> 里配置的 <code>output.path</code> 属性，所以要获取 <code>bundle.js</code>的正确 URL 是 <code>http://localhost:8080/bundle.js</code>，对应的 <code>index.html</code> 应该修改为：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">&quot;UTF-8&quot;</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;app&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br><span class="hljs-comment">&lt;!--导入 DevServer 输出的 JavaScript 文件--&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">&quot;bundle.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span><br></code></pre></td></tr></table></figure><p>Webpack 可以在启动 Webpack 时通过 <code>webpack --watch</code> 来开启监听模式，实时预览。Webpack 支持生成 Source Map，只需在启动时带上 <code>--devtool source-map</code> 参数。 加上参数重启 DevServer 后刷新页面，再打开 Chrome 浏览器的开发者工具，就可在 Sources 栏中看到可调试的源代码了。</p><p>DevServer 还有一种被称作模块热替换的刷新技术。 模块热替换能做到在不重新加载整个网页的情况下，通过将被更新过的模块替换老的模块，再重新执行一次来实现实时预览。 模块热替换相对于默认的刷新机制能提供更快的响应和更好的开发体验。 模块热替换默认是关闭的，要开启模块热替换，你只需在启动 DevServer 时带上 <code>--hot</code> 参数，重启 DevServer 后再去更新文件就能体验到模块热替换的神奇了。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> config=<span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;./webpack.config.js&#x27;</span>);<br><span class="hljs-keyword">const</span> complier=wepack(config)<br><span class="hljs-comment">// 在node中使用webpack的方法。</span><br></code></pre></td></tr></table></figure><h3 id="核心概念"><a href="#核心概念" class="headerlink" title="核心概念"></a>核心概念</h3><p> Webpack 有以下几个核心概念。</p><ul><li><strong>Entry</strong>：入口，Webpack 执行构建的第一步将从 Entry 开始，可抽象成输入。</li><li><strong>Module</strong>：模块，在 Webpack 里一切皆模块，一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。</li><li><strong>Chunk</strong>：代码块，一个 Chunk 由多个模块组合而成，用于代码合并与分割。</li><li><strong>Loader</strong>：模块转换器，用于把模块原内容按照需求转换成新内容。</li><li><strong>Plugin</strong>：扩展插件，在 Webpack 构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要的事情。</li><li><strong>Output</strong>：输出结果，在 Webpack 经过一系列处理并得出最终想要的代码后输出结果。</li></ul><p>Webpack 启动后会从 Entry 里配置的 Module 开始递归解析 Entry 依赖的所有 Module。 每找到一个 Module， 就会根据配置的 Loader 去找出对应的转换规则，对 Module 进行转换后，再解析出当前 Module 依赖的 Module。 这些模块会以 Entry 为单位进行分组，一个 Entry 和其所有依赖的 Module 被分到一个组也就是一个 Chunk。最后 Webpack 会把所有 Chunk 转换成文件输出。 在整个流程中 Webpack 会在恰当的时机执行 Plugin 里定义的逻辑。</p><h2 id="webpack的配置"><a href="#webpack的配置" class="headerlink" title="webpack的配置"></a>webpack的配置</h2><h3 id="Entry"><a href="#Entry" class="headerlink" title="Entry"></a>Entry</h3><p>entry 是配置模块的入口，可抽象成输入， Webpack 执行构建的第 步将从入 口开始，搜寻及递归解析出所有入口依赖的模块。entry 配置是必填的，若不填则将导致 Webpack 报错、退出。</p><h4 id="context"><a href="#context" class="headerlink" title="context"></a>context</h4><p>Webpack 在寻找相对路径的文件时会以 context 为根目录， context 默认为执行启动<br>Webpack 时所在的当前工作目录。如果想改变 context 的默认配置，则可以在配置文件里这<br>样设置它</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-built_in">module</span>.exports = &#123;<br><span class="hljs-attr">context</span>: path.resolve(__dirname,<span class="hljs-string">&#x27;app&#x27;</span>)<br>&#125;<br></code></pre></td></tr></table></figure><h3 id="loader"><a href="#loader" class="headerlink" title="loader"></a>loader</h3><h4 id="file-loader"><a href="#file-loader" class="headerlink" title="file-loader"></a>file-loader</h4><p>就会拷贝到打包文件夹内</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-built_in">module</span>.exports = &#123;<br>  <span class="hljs-attr">mode</span>: <span class="hljs-string">&quot;development&quot;</span>,<br>  <span class="hljs-attr">entry</span>: &#123;<br>    <span class="hljs-attr">main</span>: <span class="hljs-string">&quot;./src/index.js&quot;</span><br>  &#125;,<br>  <span class="hljs-attr">module</span>: &#123;<br>    <span class="hljs-attr">rules</span>: [<br>      &#123;<br>        <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.jpg$/</span>,<br>        use: &#123;<br>          <span class="hljs-attr">loader</span>: <span class="hljs-string">&quot;file-loader&quot;</span>,<br>          <span class="hljs-attr">options</span>: &#123;<br>            <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;[name].[ext]&quot;</span><br>          &#125;<br>        &#125;<br>      &#125;<br>    ]<br>  &#125;,<br>  <span class="hljs-attr">output</span>: &#123;<br>    <span class="hljs-attr">filename</span>: <span class="hljs-string">&quot;main.js&quot;</span>,<br>    <span class="hljs-attr">path</span>: path.resolve(__dirname, <span class="hljs-string">&quot;dist&quot;</span>)<br>  &#125;<br>&#125;;<br></code></pre></td></tr></table></figure><h4 id="url-loader"><a href="#url-loader" class="headerlink" title="url-loader"></a>url-loader</h4><p>会变成 base64，在 js 文件中直接加载</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">&quot;path&quot;</span>);<br><span class="hljs-built_in">module</span>.exports = &#123;<br>  <span class="hljs-attr">mode</span>: <span class="hljs-string">&quot;development&quot;</span>,<br>  <span class="hljs-attr">entry</span>: &#123;<br>    <span class="hljs-attr">main</span>: <span class="hljs-string">&quot;./src/index.js&quot;</span><br>  &#125;,<br>  <span class="hljs-attr">module</span>: &#123;<br>    <span class="hljs-attr">rules</span>: [<br>      &#123;<br>        <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.(jpg|png|gif)$/</span>,<br>        use: &#123;<br>          <span class="hljs-attr">loader</span>: <span class="hljs-string">&quot;url-loader&quot;</span>,<br>          <span class="hljs-attr">options</span>: &#123;<br>            <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;[name].[ext]&quot;</span>,<br>            <span class="hljs-attr">outputPath</span>: <span class="hljs-string">&quot;images/&quot;</span>,<br>            <span class="hljs-attr">limit</span>: <span class="hljs-number">1024</span><br>          &#125;<br>        &#125;<br>      &#125;<br>    ]<br>  &#125;,<br>  <span class="hljs-attr">output</span>: &#123;<br>    <span class="hljs-attr">filename</span>: <span class="hljs-string">&quot;main.js&quot;</span>,<br>    <span class="hljs-attr">path</span>: path.resolve(__dirname, <span class="hljs-string">&quot;dist&quot;</span>)<br>  &#125;<br>&#125;;<br></code></pre></td></tr></table></figure><h3 id="css-loader-style-loader"><a href="#css-loader-style-loader" class="headerlink" title="css-loader style-loader"></a>css-loader style-loader</h3><h2 id="Webpack-进阶"><a href="#Webpack-进阶" class="headerlink" title="Webpack 进阶"></a>Webpack 进阶</h2><h2 id="Webpack-配置及案例"><a href="#Webpack-配置及案例" class="headerlink" title="Webpack 配置及案例"></a>Webpack 配置及案例</h2><h2 id="Webpack-原理及脚手架"><a href="#Webpack-原理及脚手架" class="headerlink" title="Webpack 原理及脚手架"></a>Webpack 原理及脚手架</h2>]]></content>
    
    
    
    <tags>
      
      <tag>Webpack</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>JavaScript面向对象编程总结</title>
    <link href="/blog/posts/9364fbab.html"/>
    <url>/blog/posts/9364fbab.html</url>
    
    <content type="html"><![CDATA[<h2 id="What-is-Object-oriented-Programming"><a href="#What-is-Object-oriented-Programming" class="headerlink" title="What is Object-oriented Programming"></a>What is Object-oriented Programming</h2><img src="/blog/posts/9364fbab/1569242664504.png" class="" width="1569242664504"><span id="more"></span><div class="note info">OOP是一种编程范例，或者编程风格，这是围绕对象而不是函数</div>面向对象编程中的四个核心概念`Encapsulation`---封装 `Abstraction`---抽象 `Inheritance`---继承 `Polymorphism`---多态区别与面向过程编程<img src="/blog/posts/9364fbab/1568869135169.png" class="" width="1568869135169"><img src="/blog/posts/9364fbab/1568868905016.png" class="" width="1568868905016"><div class="note danger">改变了其中一个函数，然后其他几个函数可能就奔溃了，这就是我们说的意大利面条代码。函数之间深层次的关联变成了各种问题的来源，OOP就应运而生。</div><img src="/blog/posts/9364fbab/1568869159417.png" class="" width="1568869159417"><p>OOP 就一组相关的变量和函数组成合成一个单元，我们称之为对象(object)。把里面的函数称为方法，里面的变量称之为属性。</p><img src="/blog/posts/9364fbab/1568869302595.png" class="" width="1568869302595"><div class="note info">最好的函数是那些没有参数的函数，参数个数越少，使用和维护就越简单。这就是封装！</div><img src="/blog/posts/9364fbab/1568869634914.png" class="" width="1568869634914"><img src="/blog/posts/9364fbab/1568869889043.png" class="" width="1568869889043"><div class="note info">多态意味着多种形态</div><img src="/blog/posts/9364fbab/1568873343511.png" class="" width="1568873343511"><img src="/blog/posts/9364fbab/1568873363029.png" class="" width="1568873363029"><div class="note success">使用封装重新组合的相关的变量和函数，这样可以减少复杂性，可以在程序的不同部分重用这些对象    或者在不同程序中，通过抽象，隐藏细节和复杂性，只显示必要性，这种技术降低了复杂性，也隔离了代码更改的影响。    继承让我们消除多余的代码    多态性可以避免写出复杂丑陋的选择性代码</div><img src="/blog/posts/9364fbab/1568873756718.png" class="" width="1568873756718"><h2 id="原型与原型继承"><a href="#原型与原型继承" class="headerlink" title="原型与原型继承"></a>原型与原型继承</h2><p>原型<code>Prototypes</code> 和 原型继承<code>Prototyical Inheritance</code></p><p>JavaScript 中的类并不同于 Java 或者 c#中的类，因为 Javascript 是动态语言，所以类的本质上是更像是为了配合原型和原型继承所采取的必要的技术。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">//使用字面量创建对象</span><br><span class="hljs-keyword">const</span> circle = &#123;&#125;;<br><span class="hljs-comment">//一个Javascript的对象实际上是一组键值对的集合</span><br><span class="hljs-comment">//使用字面量语法来创建多个对象是有问题的，那就是对象的行为性，就像人一样可以做很多事就叫做行为性。</span><br><span class="hljs-comment">//解决方法就是用工厂函数（factory）或者构造函数（constructor）</span><br><br><span class="hljs-comment">//工厂函数</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createCircle</span>(<span class="hljs-params">radius</span>) </span>&#123;<br>  <span class="hljs-keyword">return</span> &#123;<br>    radius,<br>    <span class="hljs-function"><span class="hljs-title">draw</span>(<span class="hljs-params"></span>)</span> &#123;<br>      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;draw&quot;</span>);<br>    &#125;<br>  &#125;;<br>&#125;<br><span class="hljs-keyword">const</span> circle2 = createCircle(<span class="hljs-number">1</span>);<br><span class="hljs-comment">// 构造函数</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Circle</span>(<span class="hljs-params">radius</span>) </span>&#123;<br>  <span class="hljs-built_in">this</span>.radius = raius;<br>  <span class="hljs-built_in">this</span>.draw = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;<br>    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;draw&quot;</span>);<br>  &#125;;<br>&#125;<br><span class="hljs-keyword">const</span> circle3 = <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">2</span>);<br><span class="hljs-comment">//当我们使用new操作符调用一个函数时，3件事发生了</span><br><span class="hljs-comment">//首先new操作符创建了一个空对象，然后设置this指向这个对象，最后返回这个对象</span><br></code></pre></td></tr></table></figure><p>补充：</p><p><a href="https://www.baidu.com/s?wd=字面量&amp;tn=SE_PcZhidaonwhc_ngpagmjz&amp;rsv_dl=gh_pc_zhidao">字面量</a>是变量的字符串表示形式。它不是一种值，而是一种变量记法。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> a = <span class="hljs-number">1</span>; <span class="hljs-comment">//1是字面量</span><br><span class="hljs-keyword">const</span> b = <span class="hljs-string">&quot;hello world&quot;</span>; <span class="hljs-comment">//hello world是字面量</span><br><span class="hljs-keyword">const</span> c = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]; <span class="hljs-comment">//[1,2,3]是字面量</span><br><span class="hljs-keyword">const</span> d = &#123; <span class="hljs-attr">foo</span>: <span class="hljs-string">&quot;bar&quot;</span> &#125;; <span class="hljs-comment">//&#123;&quot;foo&quot;:&quot;bar&quot;&#125;是字面量</span><br></code></pre></td></tr></table></figure><p>每个对象都有构造函数属性</p><p>这个属性引用了用来创建这个对象的构造函数</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">new</span> <span class="hljs-built_in">String</span>(); <span class="hljs-comment">// &#x27;&#x27;,&quot; &quot;,``</span><br><span class="hljs-keyword">new</span> <span class="hljs-built_in">Boolean</span>(); <span class="hljs-comment">// true ,false</span><br><span class="hljs-keyword">new</span> <span class="hljs-built_in">Number</span>(); <span class="hljs-comment">//1,2,3,4,5,6</span><br><span class="hljs-keyword">new</span> <span class="hljs-built_in">Object</span>(); <span class="hljs-comment">//&#123;&#125;</span><br></code></pre></td></tr></table></figure><img src="/blog/posts/9364fbab/1568897883740.png" class="" width="1568897883740"><div class="note info"><p>值类型复制值</p><p>对象或者引用类型复制他们的引用</p></div><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">let</span> number=<span class="hljs-number">10</span>;<br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">increase</span>(<span class="hljs-params">number</span>)</span>&#123;<br>    number++;<br>&#125;<br>increase(number);<br><span class="hljs-built_in">console</span>.log(number) <span class="hljs-comment">//10</span><br><br><span class="hljs-keyword">let</span> object=&#123;<span class="hljs-attr">value</span>:<span class="hljs-number">10</span>&#125;;<br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">increase</span>(<span class="hljs-params">object</span>)</span>&#123;<br>object.value++;<br>&#125;<br>increase(object);<br><span class="hljs-built_in">console</span>.log(object) <span class="hljs-comment">//&#123;value:11&#125;</span><br><br><span class="hljs-string">``</span><span class="hljs-string">``</span><br><br>不知道要访问的对象名称属性，是在运行时产生的，可以使用方括号的语法,或者属性名不符合命名规则时。<br><br>抽象意味着我们应该隐藏细节和复杂部分，只显示或者暴露必要的部分<br><br><span class="hljs-string">``</span><span class="hljs-string">`js</span><br><span class="hljs-string">this.defaultLocaltion=&#123;x:0,y:1&#125; // ====&gt; let defaultLocaltion=&#123;x:0,y:1&#125;</span><br><span class="hljs-string">Object.defineProperty(this,&#x27;defaultLocaltion&#x27;,&#123;</span><br><span class="hljs-string">    get()&#123;</span><br><span class="hljs-string">        return defaultLocaltion</span><br><span class="hljs-string">    &#125;,</span><br><span class="hljs-string">    set(value)&#123;</span><br><span class="hljs-string">        defaultLocaltion=value</span><br><span class="hljs-string">    &#125;</span><br><span class="hljs-string">&#125;)</span><br></code></pre></td></tr></table></figure><div class="note info">Javascript 中没有类，只有对象，那只有对象的时候如何引入继承？答案是原型。原型可以理解为一个对象的父母，原型就是一般的对象。</div><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> person = &#123; <span class="hljs-attr">name</span>: <span class="hljs-string">&quot;hello&quot;</span> &#125;;<br><span class="hljs-built_in">Object</span>.defineProperty(person, <span class="hljs-string">&quot;name&quot;</span>, &#123;<br><span class="hljs-attr">writable</span>: <span class="hljs-literal">false</span>,<br><span class="hljs-attr">enumerable</span>: <span class="hljs-literal">true</span>,<br><span class="hljs-attr">configurable</span>: <span class="hljs-literal">false</span><br>&#125;);<br><span class="hljs-keyword">delete</span> person.name;<br><span class="hljs-built_in">console</span>.log(person); <span class="hljs-comment">// &#123; name: &#x27;hello&#x27; &#125;</span><br></code></pre></td></tr></table></figure><p>获得对象原型的方法是调用 Object 对象的 getPrototypeOf 方法</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Circle</span>(<span class="hljs-params">radius</span>) </span>&#123;<br>  <span class="hljs-built_in">this</span>.radius = radius;<br>&#125;<br><span class="hljs-keyword">const</span> circle = <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">1</span>);<br>Circle.prototype; <span class="hljs-comment">//这是构造函数创建的对象的父母真身</span><br>circle.__proto__ === Circle.prototype; <span class="hljs-comment">// true</span><br></code></pre></td></tr></table></figure><p>Object.keys 只返回实例的成员</p><p>for-in 循环返回所有的成员，对象实例本身的和它的原型的</p><img src="/blog/posts/9364fbab/1568956155416.png" class="" width="1568956155416"><img src="/blog/posts/9364fbab/1568982071111.png" class="" width="1568982071111"><p>在 Javascript 中，有个函数可以从给定的原型创建对象，就是 Object.create(第一个参数是用作创建的原型)</p><p>Javascript 里每个对象都有一个构造函数属性，能返回用以创建这个对象的构造函数</p><p>避免创建层级式继承关系，因为这十分脆弱。如果要用继承特性，最好维持在一级。好的组合胜过继承。</p><img src="/blog/posts/9364fbab/1568984473440.png" class="" width="1568984473440"><img src="/blog/posts/9364fbab/1568984534425.png" class="" width="1568984534425"><p><div class="note info">Object.assign()可以用这个方法从一个对象拷贝所有成员到另外一个对象</div><br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><code class="hljs javascript">    <span class="hljs-keyword">const</span> canEat = &#123;<br>      <span class="hljs-attr">eat</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;<br>        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;eating&quot;</span>);<br>      &#125;<br>    &#125;;<br>    <span class="hljs-keyword">const</span> canWalk = &#123;<br>      <span class="hljs-attr">walk</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;<br>        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;eating&quot;</span>);<br>      &#125;<br>    &#125;;<br>    <span class="hljs-keyword">const</span> canSwin = &#123;<br>      <span class="hljs-attr">swin</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>&#123;<br>        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;swining&quot;</span>);<br>      &#125;<br>    &#125;;<br>    <span class="hljs-comment">//   const person = Object.assign(&#123;&#125;, canEat, canWalk);</span><br><span class="hljs-comment">// 空对象实际上变成了2个对象的组合</span><br>    <span class="hljs-comment">//   console.log(person);</span><br>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mixins</span>(<span class="hljs-params">target, ...sources</span>) </span>&#123;<br>      <span class="hljs-built_in">Object</span>.assign(target.prototype, ...sources);<br>    &#125;<br>    <span class="hljs-comment">//   function Person() &#123;&#125;</span><br>    <span class="hljs-comment">//   Object.assign(Person.prototype, canEat, canWalk);</span><br>    <span class="hljs-comment">//   console.log(new Person());</span><br>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Dog</span>(<span class="hljs-params"></span>) </span>&#123;&#125;<br>    mixins(Dog, canEat, canWalk);<br>    <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">new</span> Dog());<br>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">GoldFish</span>(<span class="hljs-params"></span>) </span>&#123;&#125;<br>    mixins(GoldFish, canEat, canSwin);<br>    <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">new</span> GoldFish());<br></code></pre></td></tr></table></figure></p><h2 id="ES6"><a href="#ES6" class="headerlink" title="ES6"></a>ES6</h2><p>函数声明 <code>funciton sayHello()&#123;&#125;</code> 结尾不需要加分号，函数声明是置顶的。</p><p>函数表达式<code>const sayGoodbye=function()&#123;&#125;</code> 结尾需要加分号，不会被置顶。</p><p>不同于函数，类声明和类表达式都不会被置顶</p><p>实例方法和静态方法</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Circle</span> </span>&#123;<br>  <span class="hljs-function"><span class="hljs-title">constructor</span>(<span class="hljs-params">radius</span>)</span> &#123;<br>    <span class="hljs-built_in">this</span>.radius = radius;<br>  &#125;<br>  <span class="hljs-comment">// Instance Method</span><br>  <span class="hljs-function"><span class="hljs-title">draw</span>(<span class="hljs-params"></span>)</span> &#123;&#125;<br>  <span class="hljs-comment">// Static Mthod</span><br>  <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-title">parse</span>(<span class="hljs-params">str</span>)</span> &#123;<br>    <span class="hljs-keyword">const</span> &#123; radius &#125; = <span class="hljs-built_in">JSON</span>.parse(str);<br>    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Circle(radius);<br>  &#125;<br>&#125;<br><span class="hljs-keyword">const</span> circle = Circle.parse(<span class="hljs-string">&#x27;&#123;&quot;radius&quot;:1&#125;&#x27;</span>);<br><span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;Go: circle&quot;</span>, circle); <span class="hljs-comment">// Go: circle Circle &#123; radius: 1 &#125;</span><br></code></pre></td></tr></table></figure><p>所以我们用静态方法的方式创建不属于具体实例的工具函数</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> c = <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">2</span>);<br><span class="hljs-comment">// Method call</span><br>c.draw(); <span class="hljs-comment">// Circle &#123; radius: 2 &#125;</span><br><span class="hljs-keyword">const</span> draw = c.draw;<br><span class="hljs-comment">// Function call</span><br>draw(); <span class="hljs-comment">// undefined</span><br></code></pre></td></tr></table></figure><p>ES6 私有</p><p>第一种是用在命名的时候加下划线</p><p>第二种是使用 Symbol</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> _radius = <span class="hljs-built_in">Symbol</span>();<br><span class="hljs-keyword">const</span> _draw = <span class="hljs-built_in">Symbol</span>();<br><span class="hljs-comment">// Symbol() 是一个函数，能创建一个Symbol，这个不是构造函数，不能在前面加new修饰符，这样会报错</span><br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Circle</span> </span>&#123;<br>  <span class="hljs-function"><span class="hljs-title">constructor</span>(<span class="hljs-params">radius</span>)</span> &#123;<br>    <span class="hljs-built_in">this</span>[_radius] = radius;<br>  &#125;<br>  [_draw]() &#123;<br>    <span class="hljs-comment">// 计算生成属性</span><br>  &#125;<br>&#125;<br><br><span class="hljs-keyword">const</span> c = <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">1</span>);<br><span class="hljs-keyword">const</span> key = <span class="hljs-built_in">Object</span>.getOwnPropertySymbols(c)[<span class="hljs-number">0</span>];<br><span class="hljs-built_in">console</span>.log(c[key]); <span class="hljs-comment">// 1</span><br></code></pre></td></tr></table></figure><p>第三种是使用 WeakMap</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> _radius = <span class="hljs-keyword">new</span> <span class="hljs-built_in">WeakMap</span>();<br><span class="hljs-keyword">const</span> _move = <span class="hljs-keyword">new</span> <span class="hljs-built_in">WeakMap</span>();<br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Circle</span> </span>&#123;<br>  <span class="hljs-function"><span class="hljs-title">constructor</span>(<span class="hljs-params">radius</span>)</span> &#123;<br>    _radius.set(<span class="hljs-built_in">this</span>, radius);<br>    _move.set(<span class="hljs-built_in">this</span>, <span class="hljs-function">() =&gt;</span> &#123;<br>      <span class="hljs-comment">// 箭头函数将从调用它的构造器继承过来，在这个构造器里。this是circle对象实例的引用。</span><br>      <span class="hljs-comment">// 当我们在构造器函数里使用箭头函数时，this不会重新绑定，也不会重设，直接从构造器继承</span><br>      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;moving&quot;</span>, <span class="hljs-built_in">this</span>);<br>    &#125;);<br>  &#125;<br>  <span class="hljs-function"><span class="hljs-title">draw</span>(<span class="hljs-params"></span>)</span> &#123;<br>    _move.get(<span class="hljs-built_in">this</span>)();<br>    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;drawing...&quot;</span>);<br>  &#125;<br>&#125;<br><br><span class="hljs-keyword">const</span> c = <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">2</span>);<br>c.draw();<br></code></pre></td></tr></table></figure><p>getter&amp;&amp;setter</p><p>方法重写</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Shape</span> </span>&#123;<br>  <span class="hljs-function"><span class="hljs-title">move</span>(<span class="hljs-params"></span>)</span> &#123;<br>    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;moving....&quot;</span>);<br>  &#125;<br>&#125;<br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Circle</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Shape</span> </span>&#123;<br>  <span class="hljs-function"><span class="hljs-title">move</span>(<span class="hljs-params"></span>)</span> &#123;<br>    <span class="hljs-built_in">super</span>.move();<br>    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;circle move&quot;</span>);<br>  &#125;<br>&#125;<br><br><span class="hljs-keyword">const</span> c = <span class="hljs-keyword">new</span> Circle();<br></code></pre></td></tr></table></figure><img src="/blog/posts/9364fbab/1569201901993.png" class="" width="1569201901993"><p>AMD，也就是异步模块定义，主要是在浏览器程序中使用。</p><img src="/blog/posts/9364fbab/1569202170920.png" class="" width="1569202170920"><img src="/blog/posts/9364fbab/1569202287766.png" class="" width="1569202287766"><p>CommonJS</p><blockquote><p>class Circle{</p><p>}</p><p>module.exports.Circle=Circle</p><p>只需要引入一个模块时，可以简化代码<code>module.exports=Circle</code></p><p>引入时，使用 require 函数。</p><p>所以时 CommonJS 定义了 require 函数和 module 函数，这是 CommonJS 当中的语法。</p></blockquote><p>ES6</p><blockquote><p>export &amp;&amp; import</p><script type="module" src="../"></script></blockquote><p>在模块化之前，要记住一个首要原则，高度关联的东西应该放在一起。就好比在厨房放置了杯子盘子勺子等餐具，不应该把衣服存放在厨房，这就是高度关联。这就是编程中说的 Cohesion(内聚)。</p><img src="/blog/posts/9364fbab/1569203914069.png" class="" width="1569203914069"><h2 id="Webpack"><a href="#Webpack" class="headerlink" title="Webpack"></a>Webpack</h2><ul><li>npm i -g webpack-cli</li><li>webpack-cli init</li><li>npm init —yes</li></ul><p><a href="https://www.bilibili.com/video/av35179218/?p=1">完结</a></p>]]></content>
    
    
    
    <tags>
      
      <tag>JavaScript</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>mapboxgl实现marker的聚类</title>
    <link href="/blog/posts/a2d9d205.html"/>
    <url>/blog/posts/a2d9d205.html</url>
    
    <content type="html"><![CDATA[<img src="/blog/posts/a2d9d205/1568697066499.png" class="" width="1568697066499"><img src="/blog/posts/a2d9d205/1568697307582.png" class="" width="1568697307582">]]></content>
    
    
    
    <tags>
      
      <tag>Mapboxgl</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>vue学习与总结</title>
    <link href="/blog/posts/3d0c447.html"/>
    <url>/blog/posts/3d0c447.html</url>
    
    <content type="html"><![CDATA[<h2 id="插值表达式"><a href="#插值表达式" class="headerlink" title="插值表达式"></a>插值表达式</h2><ul><li>v-cloak</li><li>v-text</li><li>v-html</li></ul><blockquote><p>使用 v-cloak 能够解决 插值表达式闪烁的问题，<code>[v-cloak] &#123;display: none;&#125;</code>。默认 v-text 是没有闪烁问题的，v-text 会覆盖元素中原本的内容，但是插值表达式 只会替换自己的这个占位符，不会把整个元素的内容清空</p></blockquote><ul><li><p>v-bind(缩写:)</p></li><li><p>v-on(缩写@)</p></li><li><p>v-model 只能用于表单元素</p></li><li><p>v-for</p></li><li><p>v-if</p></li><li><p>v-show</p><blockquote><p>一般来说，v-if 有更高的切换消耗而 v-show 有更高的初始渲染消耗。因此，如果需要频繁切换，v-show 较好，如果运行时条件不太可能改变 v-if 较好</p></blockquote></li></ul><h2 id="事件修饰符"><a href="#事件修饰符" class="headerlink" title="事件修饰符"></a>事件修饰符</h2><span id="more"></span><ul><li>.stop 阻止冒泡</li><li>.prevent 阻止默认事件</li><li>.capture 添加事件侦听器时使用事件捕获模式</li><li>.self 只当事件在该元素本身（比如不是子元素）触发时触发回调</li><li>.once 事件只触发一次</li></ul><h2 id="在-Vue-中使用样式"><a href="#在-Vue-中使用样式" class="headerlink" title="在 Vue 中使用样式"></a>在 Vue 中使用样式</h2><h3 id="使用-class-样式"><a href="#使用-class-样式" class="headerlink" title="使用 class 样式"></a>使用 class 样式</h3><ol><li>数组</li></ol><figure class="highlight angelscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs angelscript">&lt;h1 :<span class="hljs-keyword">class</span>=&quot;[&#x27;<span class="hljs-symbol">red</span>&#x27;, &#x27;<span class="hljs-symbol">thin</span>&#x27;]&quot;&gt;这是一个邪恶的<span class="hljs-symbol">H1</span>&lt;/<span class="hljs-symbol">h1</span>&gt;<br></code></pre></td></tr></table></figure><ol><li>数组中使用三元表达式</li></ol><figure class="highlight angelscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs angelscript">&lt;h1 :<span class="hljs-keyword">class</span>=&quot;[&#x27;<span class="hljs-symbol">red</span>&#x27;, &#x27;<span class="hljs-symbol">thin</span>&#x27;, <span class="hljs-symbol">isactive</span>?&#x27;<span class="hljs-symbol">active</span>&#x27;:&#x27;&#x27;]&quot;&gt;这是一个邪恶的<span class="hljs-symbol">H1</span>&lt;/<span class="hljs-symbol">h1</span>&gt;<br></code></pre></td></tr></table></figure><ol><li>数组中嵌套对象</li></ol><figure class="highlight dust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs dust"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">:class</span>=<span class="hljs-string">&quot;[&#x27;red&#x27;, &#x27;thin&#x27;, </span></span></span><span class="hljs-template-variable">&#123;&#x27;active&#x27;: isactive&#125;</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">]&quot;</span>&gt;</span>这是一个邪恶的H1<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span><br></code></pre></td></tr></table></figure><ol><li>直接使用对象</li></ol><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs actionscript">&lt;h1 :<span class="hljs-class"><span class="hljs-keyword">class</span>=&quot;</span>&#123;red:<span class="hljs-literal">true</span>, italic:<span class="hljs-literal">true</span>, active:<span class="hljs-literal">true</span>, thin:<span class="hljs-literal">true</span>&#125;<span class="hljs-string">&quot;&gt;这是一个邪恶的H1&lt;/h1&gt;</span><br></code></pre></td></tr></table></figure><h3 id="使用内联样式"><a href="#使用内联样式" class="headerlink" title="使用内联样式"></a>使用内联样式</h3><ol><li>直接在元素上通过 <code>:style</code> 的形式，书写样式对象</li></ol><figure class="highlight dust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs dust"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">:style</span>=<span class="hljs-string">&quot;</span></span></span><span class="hljs-template-variable">&#123;color: &#x27;red&#x27;, &#x27;font-size&#x27;: &#x27;40px&#x27;&#125;</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">&quot;</span>&gt;</span>这是一个善良的H1<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span><br></code></pre></td></tr></table></figure><ol><li>将样式对象，定义到 <code>data</code> 中，并直接引用到 <code>:style</code> 中</li></ol><ul><li>在 data 上定义样式：</li></ul><figure class="highlight xl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs xl"><span class="hljs-keyword">data</span>: &#123;<br>        h1StyleObj: &#123; <span class="hljs-built_in">color</span>: <span class="hljs-string">&#x27;red&#x27;</span>, <span class="hljs-string">&#x27;font-size&#x27;</span>: <span class="hljs-string">&#x27;40px&#x27;</span>, <span class="hljs-string">&#x27;font-weight&#x27;</span>: <span class="hljs-string">&#x27;200&#x27;</span> &#125;<br>&#125;<br></code></pre></td></tr></table></figure><ul><li>在元素中，通过属性绑定的形式，将样式对象应用到元素中：</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">:style</span>=<span class="hljs-string">&quot;h1StyleObj&quot;</span>&gt;</span>这是一个善良的H1<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span><br></code></pre></td></tr></table></figure><ol><li>在 <code>:style</code> 中通过数组，引用多个 <code>data</code> 上的样式对象</li></ol><ul><li>在 data 上定义样式：</li></ul><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs css">data: &#123;<br>        h1StyleObj: &#123; color: <span class="hljs-string">&#x27;red&#x27;</span>, <span class="hljs-string">&#x27;font-size&#x27;</span>: <span class="hljs-string">&#x27;40px&#x27;</span>, <span class="hljs-string">&#x27;font-weight&#x27;</span>: <span class="hljs-string">&#x27;200&#x27;</span> &#125;,<br>        h1StyleObj2: &#123; fontStyle: <span class="hljs-string">&#x27;italic&#x27;</span> &#125;<br>&#125;<br></code></pre></td></tr></table></figure><ul><li>在元素中，通过属性绑定的形式，将样式对象应用到元素中：</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">:style</span>=<span class="hljs-string">&quot;[h1StyleObj, h1StyleObj2]&quot;</span>&gt;</span>这是一个善良的H1<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span><br></code></pre></td></tr></table></figure><h2 id="Vue-指令之v-if和v-show"><a href="#Vue-指令之v-if和v-show" class="headerlink" title="Vue 指令之v-if和v-show"></a>Vue 指令之<code>v-if</code>和<code>v-show</code></h2><blockquote><p>一般来说，v-if 有更高的切换消耗而 v-show 有更高的初始渲染消耗。因此，如果需要频繁切换 v-show 较好，如果在运行时条件不大可能改变 v-if 较好。</p></blockquote><h2 id="过滤器"><a href="#过滤器" class="headerlink" title="过滤器"></a>过滤器</h2><p>概念：Vue.js 允许你自定义过滤器，<strong>可被用作一些常见的文本格式化</strong>。过滤器可以用在两个地方：<strong>mustache 插值和 v-bind 表达式</strong>。过滤器应该被添加在 JavaScript 表达式的尾部，由“管道”符指示；</p><h3 id="私有过滤器"><a href="#私有过滤器" class="headerlink" title="私有过滤器"></a>私有过滤器</h3><ol><li>HTML 元素：</li></ol><figure class="highlight handlebars"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs handlebars"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span></span><span class="hljs-template-variable">&#123;&#123;<span class="hljs-name">item.ctime</span> | dataFormat(<span class="hljs-name">&#x27;yyyy-mm-dd&#x27;</span>)&#125;&#125;</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span></span><br><span class="xml"></span><br></code></pre></td></tr></table></figure><ol><li>私有 <code>filters</code> 定义方式：</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><code class="hljs javascript">filters: &#123; <span class="hljs-comment">// 私有局部过滤器，只能在 当前 VM 对象所控制的 View 区域进行使用</span><br><br>    <span class="hljs-function"><span class="hljs-title">dataFormat</span>(<span class="hljs-params">input, pattern = <span class="hljs-string">&quot;&quot;</span></span>)</span> &#123; <span class="hljs-comment">// 在参数列表中 通过 pattern=&quot;&quot; 来指定形参默认值，防止报错</span><br><br>      <span class="hljs-keyword">var</span> dt = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(input);<br><br>      <span class="hljs-comment">// 获取年月日</span><br><br>      <span class="hljs-keyword">var</span> y = dt.getFullYear();<br><br>      <span class="hljs-keyword">var</span> m = (dt.getMonth() + <span class="hljs-number">1</span>).toString().padStart(<span class="hljs-number">2</span>, <span class="hljs-string">&#x27;0&#x27;</span>);<br><br>      <span class="hljs-keyword">var</span> d = dt.getDate().toString().padStart(<span class="hljs-number">2</span>, <span class="hljs-string">&#x27;0&#x27;</span>);<br><br><br><br>      <span class="hljs-comment">// 如果 传递进来的字符串类型，转为小写之后，等于 yyyy-mm-dd，那么就返回 年-月-日</span><br><br>      <span class="hljs-comment">// 否则，就返回  年-月-日 时：分：秒</span><br><br>      <span class="hljs-keyword">if</span> (pattern.toLowerCase() === <span class="hljs-string">&#x27;yyyy-mm-dd&#x27;</span>) &#123;<br><br>        <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">$&#123;y&#125;</span>-<span class="hljs-subst">$&#123;m&#125;</span>-<span class="hljs-subst">$&#123;d&#125;</span>`</span>;<br><br>      &#125; <span class="hljs-keyword">else</span> &#123;<br><br>        <span class="hljs-comment">// 获取时分秒</span><br><br>        <span class="hljs-keyword">var</span> hh = dt.getHours().toString().padStart(<span class="hljs-number">2</span>, <span class="hljs-string">&#x27;0&#x27;</span>);<br><br>        <span class="hljs-keyword">var</span> mm = dt.getMinutes().toString().padStart(<span class="hljs-number">2</span>, <span class="hljs-string">&#x27;0&#x27;</span>);<br><br>        <span class="hljs-keyword">var</span> ss = dt.getSeconds().toString().padStart(<span class="hljs-number">2</span>, <span class="hljs-string">&#x27;0&#x27;</span>);<br><br><br><br>        <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">$&#123;y&#125;</span>-<span class="hljs-subst">$&#123;m&#125;</span>-<span class="hljs-subst">$&#123;d&#125;</span> <span class="hljs-subst">$&#123;hh&#125;</span>:<span class="hljs-subst">$&#123;mm&#125;</span>:<span class="hljs-subst">$&#123;ss&#125;</span>`</span>;<br><br>      &#125;<br><br>    &#125;<br><br>  &#125;<br><br></code></pre></td></tr></table></figure><blockquote><p>使用 ES6 中的字符串新方法 String.prototype.padStart(maxLength, fillString=’’) 或 String.prototype.padEnd(maxLength, fillString=’’)来填充字符串；</p></blockquote><h3 id="全局过滤器"><a href="#全局过滤器" class="headerlink" title="全局过滤器"></a>全局过滤器</h3><figure class="highlight arcade"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><code class="hljs arcade"><span class="hljs-comment">// 定义一个全局过滤器</span><br><br>Vue.filter(<span class="hljs-string">&#x27;dataFormat&#x27;</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">input, pattern = <span class="hljs-string">&#x27;&#x27;</span></span>) </span>&#123;<br><br>  <span class="hljs-keyword">var</span> dt = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(input);<br><br>  <span class="hljs-comment">// 获取年月日</span><br><br>  <span class="hljs-keyword">var</span> y = dt.getFullYear();<br><br>  <span class="hljs-keyword">var</span> m = (dt.getMonth() + <span class="hljs-number">1</span>).toString().padStart(<span class="hljs-number">2</span>, <span class="hljs-string">&#x27;0&#x27;</span>);<br><br>  <span class="hljs-keyword">var</span> d = dt.getDate().toString().padStart(<span class="hljs-number">2</span>, <span class="hljs-string">&#x27;0&#x27;</span>);<br><br><br><br>  <span class="hljs-comment">// 如果 传递进来的字符串类型，转为小写之后，等于 yyyy-mm-dd，那么就返回 年-月-日</span><br><br>  <span class="hljs-comment">// 否则，就返回  年-月-日 时：分：秒</span><br><br>  <span class="hljs-keyword">if</span> (pattern.toLowerCase() === <span class="hljs-string">&#x27;yyyy-mm-dd&#x27;</span>) &#123;<br><br>    <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">$&#123;y&#125;</span>-<span class="hljs-subst">$&#123;m&#125;</span>-<span class="hljs-subst">$&#123;d&#125;</span>`</span>;<br><br>  &#125; <span class="hljs-keyword">else</span> &#123;<br><br>    <span class="hljs-comment">// 获取时分秒</span><br><br>    <span class="hljs-keyword">var</span> hh = dt.getHours().toString().padStart(<span class="hljs-number">2</span>, <span class="hljs-string">&#x27;0&#x27;</span>);<br><br>    <span class="hljs-keyword">var</span> mm = dt.getMinutes().toString().padStart(<span class="hljs-number">2</span>, <span class="hljs-string">&#x27;0&#x27;</span>);<br><br>    <span class="hljs-keyword">var</span> ss = dt.getSeconds().toString().padStart(<span class="hljs-number">2</span>, <span class="hljs-string">&#x27;0&#x27;</span>);<br><br><br><br>    <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">$&#123;y&#125;</span>-<span class="hljs-subst">$&#123;m&#125;</span>-<span class="hljs-subst">$&#123;d&#125;</span> <span class="hljs-subst">$&#123;hh&#125;</span>:<span class="hljs-subst">$&#123;mm&#125;</span>:<span class="hljs-subst">$&#123;ss&#125;</span>`</span>;<br><br>  &#125;<br><br>&#125;);<br><br></code></pre></td></tr></table></figure><blockquote><p>注意：当有局部和全局两个名称相同的过滤器时候，会以就近原则进行调用，即：局部过滤器优先于全局过滤器被调用！</p></blockquote><h2 id="键盘修饰符以及自定义键盘修饰符"><a href="#键盘修饰符以及自定义键盘修饰符" class="headerlink" title="键盘修饰符以及自定义键盘修饰符"></a>键盘修饰符以及自定义键盘修饰符</h2><ol><li>通过<code>Vue.config.keyCodes.名称 = 按键值</code>来自定义案件修饰符的别名：</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js">Vue.config.keyCodes.f2 = <span class="hljs-number">113</span>;<br></code></pre></td></tr></table></figure><ol><li>使用自定义的按键修饰符：</li></ol><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs routeros">&lt;input <span class="hljs-attribute">type</span>=<span class="hljs-string">&quot;text&quot;</span> <span class="hljs-attribute">v-model</span>=<span class="hljs-string">&quot;name&quot;</span> @keyup.<span class="hljs-attribute">f2</span>=<span class="hljs-string">&quot;add&quot;</span>&gt;<br></code></pre></td></tr></table></figure><h2 id="自定义指令"><a href="#自定义指令" class="headerlink" title="自定义指令"></a>自定义指令</h2><ul><li><p>使用 Vue.directive()定义全局的指令，比如 v-focus</p></li><li><p>其中 参数 1：指令的名称，定义时不需要加 v-的前缀</p></li><li><p>使用的时候必须在指令名称前面加上 v-前缀来调用</p></li><li><p>参数 2：是一个对象，这个对象上有一些指令相关的函数，这些函数可以在特定的阶段执行相关的操作</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs javascript">Vue.dirctive(<span class="hljs-string">&#x27;focus&#x27;</span>,&#123;<br><span class="hljs-attr">bind</span>:<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">el</span>)</span>&#123;<br><span class="hljs-comment">//每当指令绑定到元素上的时候会立即执行这个bind函数，只执行一次</span><br>    <span class="hljs-comment">//每个函数中的第一个参数永远是el表示被绑定的指令的那个元素，是原生的js对象</span><br>    <span class="hljs-comment">//每当指令绑定到元素上的时候会立即执行这个bind函数，只执行一次</span><br>    <span class="hljs-comment">// 和样式相关的操作，一般都可以在bind执行</span><br>&#125;,<br>    <span class="hljs-function"><span class="hljs-title">inserted</span>(<span class="hljs-params">el</span>)</span>&#123;<br>        el.focus();<br>      <span class="hljs-comment">// 和js行为相关的操作，最好在inserted中执行，防止js行为不生效</span><br>    &#125;,<br>    <span class="hljs-function"><span class="hljs-title">updated</span>(<span class="hljs-params">el</span>)</span>&#123;<br>       <span class="hljs-comment">//当Vnode更新时，会执行updated，可能会触发多次</span><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure></li><li><p>私有指令的定义</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs js">dirctives:&#123;<br>     <span class="hljs-comment">//自定义指令的简写形式，等同于定义了 bind 和 update 两个钩子函数</span><br> <span class="hljs-string">&#x27;fontsize&#x27;</span>:<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">el,binding</span>)</span>&#123;<br>         el.style.fontSize=binding.value<br>     &#125;<br> &#125;<br></code></pre></td></tr></table></figure><ol><li>自定义指令的使用方式：</li></ol><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs routeros">&lt;input <span class="hljs-attribute">type</span>=<span class="hljs-string">&quot;text&quot;</span> <span class="hljs-attribute">v-model</span>=<span class="hljs-string">&quot;searchName&quot;</span> v-focus <span class="hljs-attribute">v-color</span>=<span class="hljs-string">&quot;&#x27;red&#x27;&quot;</span> <span class="hljs-attribute">v-font-weight</span>=<span class="hljs-string">&quot;900&quot;</span>&gt;<br></code></pre></td></tr></table></figure></li></ul><h3 id="实现筛选的方式显示过滤-排序结果："><a href="#实现筛选的方式显示过滤-排序结果：" class="headerlink" title="实现筛选的方式显示过滤-排序结果："></a>实现筛选的方式显示过滤-排序结果：</h3><ul><li>筛选框绑定到 VM 实例中的 <code>searchName</code> 属性：</li></ul><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs routeros">&lt;hr&gt; 输入筛选名称：<br><br>&lt;input <span class="hljs-attribute">type</span>=<span class="hljs-string">&quot;text&quot;</span> <span class="hljs-attribute">v-model</span>=<span class="hljs-string">&quot;searchName&quot;</span>&gt;<br><br></code></pre></td></tr></table></figure><ul><li>在使用 <code>v-for</code> 指令循环每一行数据的时候，不再直接 <code>item in list</code>，而是 <code>in</code> 一个 过滤的 methods 方法，同时，把过滤条件<code>searchName</code>传递进去：</li></ul><figure class="highlight handlebars"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs handlebars"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">tbody</span>&gt;</span></span><br><span class="xml"></span><br><span class="xml">      <span class="hljs-tag">&lt;<span class="hljs-name">tr</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">&quot;item in search(searchName)&quot;</span>&gt;</span></span><br><span class="xml"></span><br><span class="xml">        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span></span><span class="hljs-template-variable">&#123;&#123;<span class="hljs-name">item.id</span>&#125;&#125;</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span></span><br><span class="xml"></span><br><span class="xml">        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span></span><span class="hljs-template-variable">&#123;&#123;<span class="hljs-name">item.name</span>&#125;&#125;</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span></span><br><span class="xml"></span><br><span class="xml">        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span></span><span class="hljs-template-variable">&#123;&#123;<span class="hljs-name">item.ctime</span>&#125;&#125;</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span></span><br><span class="xml"></span><br><span class="xml">        <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span></span><br><span class="xml"></span><br><span class="xml">          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;#&quot;</span> @<span class="hljs-attr">click.prevent</span>=<span class="hljs-string">&quot;del(item.id)&quot;</span>&gt;</span>删除<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span></span><br><span class="xml"></span><br><span class="xml">        <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span></span><br><span class="xml"></span><br><span class="xml">      <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span></span><br><span class="xml"></span><br><span class="xml">    <span class="hljs-tag">&lt;/<span class="hljs-name">tbody</span>&gt;</span></span><br><span class="xml"></span><br></code></pre></td></tr></table></figure><ul><li><code>search</code> 过滤方法中，使用 数组的 <code>filter</code> 方法进行过滤：</li></ul><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs pgsql"><span class="hljs-keyword">search</span>(<span class="hljs-type">name</span>) &#123;<br><br>  <span class="hljs-keyword">return</span> this.list.<span class="hljs-keyword">filter</span>(x =&gt; &#123;<br><br>    <span class="hljs-keyword">return</span> x.name.indexOf(<span class="hljs-type">name</span>) != <span class="hljs-number">-1</span>;<br><br>  &#125;);<br><br>&#125;<br><br></code></pre></td></tr></table></figure><h2 id="JSONP-的实现原理"><a href="#JSONP-的实现原理" class="headerlink" title="JSONP 的实现原理"></a>JSONP 的实现原理</h2><ul><li>由于浏览器安全限制，不允许 AXAJ 访问协议不同、域名不同、端口号不同——不符合同源策略的。</li><li>可以通过动态创建 script 标签的形式，把 script 标签的 src 属性指向数据接口的地址。因为 script 标签不存在跨域限制，这种数据获取方式称之为 JSONP</li><li><p>具体实现过程</p><ul><li><p>先在客户端定义一个回调方法，预定义对数据的操作；</p></li><li><p>再把这个回调方法的名称通过 URL 传参的形式提交到服务器的数据接口；</p></li><li><p>服务器数据接口组织好要发送给客户端的数据，再拿客户端传递过来的回调方法名称拼接出一个调用这个方法的字符串，发送给客户端解析执行；</p></li><li><p>客户端拿到服务器的返回的字符串之后，当作 script 脚本执行。</p></li><li><p>Node.js 实现一个 JSONP 的请求例子</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;http&#x27;</span>);<br>    <span class="hljs-comment">// 导入解析 URL 地址的核心模块</span><br>    <span class="hljs-keyword">const</span> urlModule = <span class="hljs-built_in">require</span>(<span class="hljs-string">&#x27;url&#x27;</span>);<br><br>    <span class="hljs-keyword">const</span> server = http.createServer();<br>    <span class="hljs-comment">// 监听 服务器的 request 请求事件，处理每个请求</span><br>    server.on(<span class="hljs-string">&#x27;request&#x27;</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> &#123;<br>      <span class="hljs-keyword">const</span> url = req.url;<br><br>      <span class="hljs-comment">// 解析客户端请求的URL地址</span><br>      <span class="hljs-keyword">var</span> info = urlModule.parse(url, <span class="hljs-literal">true</span>);<br><br>      <span class="hljs-comment">// 如果请求的 URL 地址是 /getjsonp ，则表示要获取JSONP类型的数据</span><br>      <span class="hljs-keyword">if</span> (info.pathname === <span class="hljs-string">&#x27;/getjsonp&#x27;</span>) &#123;<br>        <span class="hljs-comment">// 获取客户端指定的回调函数的名称</span><br>        <span class="hljs-keyword">var</span> cbName = info.query.callback;<br>        <span class="hljs-comment">// 手动拼接要返回给客户端的数据对象</span><br>        <span class="hljs-keyword">var</span> data = &#123;<br>          <span class="hljs-attr">name</span>: <span class="hljs-string">&#x27;zs&#x27;</span>,<br>          <span class="hljs-attr">age</span>: <span class="hljs-number">22</span>,<br>          <span class="hljs-attr">gender</span>: <span class="hljs-string">&#x27;男&#x27;</span>,<br>          <span class="hljs-attr">hobby</span>: [<span class="hljs-string">&#x27;吃饭&#x27;</span>, <span class="hljs-string">&#x27;睡觉&#x27;</span>, <span class="hljs-string">&#x27;运动&#x27;</span>]<br>        &#125;<br>        <span class="hljs-comment">// 拼接出一个方法的调用，在调用这个方法的时候，把要发送给客户端的数据，序列化为字符串，作为参数传递给这个调用的方法：</span><br>        <span class="hljs-keyword">var</span> result = <span class="hljs-string">`<span class="hljs-subst">$&#123;cbName&#125;</span>(<span class="hljs-subst">$&#123;<span class="hljs-built_in">JSON</span>.stringify(data)&#125;</span>)`</span>;<br>        <span class="hljs-comment">// 将拼接好的方法的调用，返回给客户端去解析执行</span><br>        res.end(result);<br>      &#125; <span class="hljs-keyword">else</span> &#123;<br>        res.end(<span class="hljs-string">&#x27;404&#x27;</span>);<br>      &#125;<br>    &#125;);<br><br>    server.listen(<span class="hljs-number">3000</span>, <span class="hljs-function">() =&gt;</span> &#123;<br>      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&#x27;server running at =http://127.0.0.1:3000&#x27;</span>);<br>    &#125;);<br></code></pre></td></tr></table></figure></li></ul></li></ul><h2 id="Vue-中的动画"><a href="#Vue-中的动画" class="headerlink" title="Vue 中的动画"></a><a href="https://cn.vuejs.org/v2/guide/transitions.html">Vue 中的动画</a></h2><h3 id="使用过渡类名"><a href="#使用过渡类名" class="headerlink" title="使用过渡类名"></a>使用过渡类名</h3><ol><li>HTML 结构：</li></ol><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;app&quot;</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;button&quot;</span> <span class="hljs-attr">value</span>=<span class="hljs-string">&quot;动起来&quot;</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">&quot;myAnimate&quot;</span>&gt;</span><br>    <span class="hljs-comment">&lt;!-- 使用 transition 将需要过渡的元素包裹起来 --&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">transition</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;fade&quot;</span>&gt;</span><br>      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-show</span>=<span class="hljs-string">&quot;isshow&quot;</span>&gt;</span>动画哦<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">transition</span>&gt;</span><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br></code></pre></td></tr></table></figure><ol><li>VM 实例：</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">// 创建 Vue 实例，得到 ViewModel</span><br><span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue(&#123;<br>  <span class="hljs-attr">el</span>: <span class="hljs-string">&#x27;#app&#x27;</span>,<br>  <span class="hljs-attr">data</span>: &#123;<br>    <span class="hljs-attr">isshow</span>: <span class="hljs-literal">false</span><br>  &#125;,<br>  <span class="hljs-attr">methods</span>: &#123;<br>    <span class="hljs-function"><span class="hljs-title">myAnimate</span>(<span class="hljs-params"></span>)</span> &#123;<br>      <span class="hljs-built_in">this</span>.isshow = !<span class="hljs-built_in">this</span>.isshow;<br>    &#125;<br>  &#125;<br>&#125;);<br><br></code></pre></td></tr></table></figure><ol><li>定义两组类样式：</li></ol><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs css"><span class="hljs-comment">/* 定义进入和离开时候的过渡状态 */</span><br>    <span class="hljs-selector-class">.fade-enter-active</span>,<br>    <span class="hljs-selector-class">.fade-leave-active</span> &#123;<br>      <span class="hljs-attribute">transition</span>: all <span class="hljs-number">0.2s</span> ease;<br>      <span class="hljs-attribute">position</span>: absolute;<br>    &#125;<br><br>    <span class="hljs-comment">/* 定义进入过渡的开始状态 和 离开过渡的结束状态 */</span><br>    <span class="hljs-selector-class">.fade-enter</span>,<br>    <span class="hljs-selector-class">.fade-leave-to</span> &#123;<br>      <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>;<br>      <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(<span class="hljs-number">100px</span>);<br>    &#125;<br></code></pre></td></tr></table></figure><h3 id="使用第三方-CSS-动画库"><a href="#使用第三方-CSS-动画库" class="headerlink" title="使用第三方 CSS 动画库"></a><a href="https://cn.vuejs.org/v2/guide/transitions.html#自定义过渡类名">使用第三方 CSS 动画库</a></h3><ol><li>导入动画类库：</li></ol><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs routeros">&lt;link <span class="hljs-attribute">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span> <span class="hljs-attribute">type</span>=<span class="hljs-string">&quot;text/css&quot;</span> <span class="hljs-attribute">href</span>=<span class="hljs-string">&quot;./lib/animate.css&quot;</span>&gt;<br></code></pre></td></tr></table></figure><ol><li>定义 transition 及属性：</li></ol><figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs applescript">&lt;transition<br>enter-active-<span class="hljs-built_in">class</span>=<span class="hljs-string">&quot;fadeInRight&quot;</span><br>    leave-active-<span class="hljs-built_in">class</span>=<span class="hljs-string">&quot;fadeOutRight&quot;</span><br>    :duration=<span class="hljs-string">&quot;&#123; enter: 500, leave: 800 &#125;&quot;</span>&gt;<br>  &lt;<span class="hljs-keyword">div</span> <span class="hljs-built_in">class</span>=<span class="hljs-string">&quot;animated&quot;</span> v-show=<span class="hljs-string">&quot;isshow&quot;</span>&gt;动画哦&lt;/<span class="hljs-keyword">div</span>&gt;<br>&lt;/transition&gt;<br></code></pre></td></tr></table></figure><h3 id="使用动画钩子函数"><a href="#使用动画钩子函数" class="headerlink" title="使用动画钩子函数"></a>使用动画钩子函数</h3><ol><li>定义 transition 组件以及三个钩子函数：</li></ol><figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs applescript">&lt;<span class="hljs-keyword">div</span> <span class="hljs-built_in">id</span>=<span class="hljs-string">&quot;app&quot;</span>&gt;<br>    &lt;input type=<span class="hljs-string">&quot;button&quot;</span> value=<span class="hljs-string">&quot;切换动画&quot;</span> @click=<span class="hljs-string">&quot;isshow = !isshow&quot;</span>&gt;<br>    &lt;transition<br>    @<span class="hljs-keyword">before</span>-enter=<span class="hljs-string">&quot;beforeEnter&quot;</span><br>    @enter=<span class="hljs-string">&quot;enter&quot;</span><br>    @<span class="hljs-keyword">after</span>-enter=<span class="hljs-string">&quot;afterEnter&quot;</span>&gt;<br>      &lt;<span class="hljs-keyword">div</span> v-<span class="hljs-keyword">if</span>=<span class="hljs-string">&quot;isshow&quot;</span> <span class="hljs-built_in">class</span>=<span class="hljs-string">&quot;show&quot;</span>&gt;OK&lt;/<span class="hljs-keyword">div</span>&gt;<br>    &lt;/transition&gt;<br>  &lt;/<span class="hljs-keyword">div</span>&gt;<br></code></pre></td></tr></table></figure><ol><li>定义三个 methods 钩子方法：</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs javascript">methods: &#123;<br>        <span class="hljs-function"><span class="hljs-title">beforeEnter</span>(<span class="hljs-params">el</span>)</span> &#123; <span class="hljs-comment">// 动画进入之前的回调</span><br>          el.style.transform = <span class="hljs-string">&#x27;translateX(500px)&#x27;</span>;<br>        &#125;,<br>        <span class="hljs-function"><span class="hljs-title">enter</span>(<span class="hljs-params">el, done</span>)</span> &#123; <span class="hljs-comment">// 动画进入完成时候的回调</span><br>          el.offsetWidth;<br>          el.style.transform = <span class="hljs-string">&#x27;translateX(0px)&#x27;</span>;<br>          done();<br>        &#125;,<br>        <span class="hljs-function"><span class="hljs-title">afterEnter</span>(<span class="hljs-params">el</span>)</span> &#123; <span class="hljs-comment">// 动画进入完成之后的回调</span><br>          <span class="hljs-built_in">this</span>.isshow = !<span class="hljs-built_in">this</span>.isshow;<br>        &#125;<br>      &#125;<br></code></pre></td></tr></table></figure><ol><li>定义动画过渡时长和样式：</li></ol><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs css"><span class="hljs-selector-class">.show</span>&#123;<br>      <span class="hljs-attribute">transition</span>: all <span class="hljs-number">0.4s</span> ease;<br>    &#125;<br></code></pre></td></tr></table></figure><h3 id="v-for-的列表过渡"><a href="#v-for-的列表过渡" class="headerlink" title="v-for 的列表过渡"></a><a href="https://cn.vuejs.org/v2/guide/transitions.html#列表的进入和离开过渡">v-for 的列表过渡</a></h3><ol><li>定义过渡样式：</li></ol><figure class="highlight dsconfig"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs dsconfig">&lt;<span class="hljs-string">style</span>&gt;<br>    .<span class="hljs-built_in">list-enter,</span><br>    .<span class="hljs-built_in">list-leave-to</span> &#123;<br>      <span class="hljs-string">opacity</span>: <span class="hljs-string">0</span>;<br>      <span class="hljs-string">transform</span>: <span class="hljs-string">translateY</span>(<span class="hljs-string">10px</span>);<br>    &#125;<br><br>    .<span class="hljs-built_in">list-enter-active,</span><br>    .<span class="hljs-built_in">list-leave-active</span> &#123;<br>      <span class="hljs-string">transition</span>: <span class="hljs-string">all</span> <span class="hljs-string">0</span>.<span class="hljs-string">3s</span> <span class="hljs-string">ease</span>;<br>    &#125;<br>&lt;/<span class="hljs-string">style</span>&gt;<br></code></pre></td></tr></table></figure><ol><li>定义 DOM 结构，其中，需要使用 transition-group 组件把 v-for 循环的列表包裹起来：</li></ol><figure class="highlight handlebars"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs handlebars"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;app&quot;</span>&gt;</span></span><br><span class="xml">  <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;text&quot;</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">&quot;txt&quot;</span> @<span class="hljs-attr">keyup.enter</span>=<span class="hljs-string">&quot;add&quot;</span>&gt;</span></span><br><span class="xml"></span><br><span class="xml">  <span class="hljs-tag">&lt;<span class="hljs-name">transition-group</span> <span class="hljs-attr">tag</span>=<span class="hljs-string">&quot;ul&quot;</span> <span class="hljs-attr">name</span>=<span class="hljs-string">&quot;list&quot;</span>&gt;</span></span><br><span class="xml">    <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">&quot;(item, i) in list&quot;</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">&quot;i&quot;</span>&gt;</span></span><span class="hljs-template-variable">&#123;&#123;<span class="hljs-name">item</span>&#125;&#125;</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span></span><br><span class="xml">  <span class="hljs-tag">&lt;/<span class="hljs-name">transition-group</span>&gt;</span></span><br><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span><br></code></pre></td></tr></table></figure><ol><li>定义 VM 中的结构：</li></ol><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs kotlin"><span class="hljs-comment">// 创建 Vue 实例，得到 ViewModel</span><br><span class="hljs-keyword">var</span> vm = new Vue(&#123;<br>  el: <span class="hljs-string">&#x27;#app&#x27;</span>,<br>  <span class="hljs-keyword">data</span>: &#123;<br>    txt: <span class="hljs-string">&#x27;&#x27;</span>,<br>    list: [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>]<br>  &#125;,<br>  methods: &#123;<br>    add() &#123;<br>      <span class="hljs-keyword">this</span>.list.push(<span class="hljs-keyword">this</span>.txt);<br>      <span class="hljs-keyword">this</span>.txt = <span class="hljs-string">&#x27;&#x27;</span>;<br>    &#125;<br>  &#125;<br>&#125;);<br></code></pre></td></tr></table></figure><h3 id="列表的排序过渡"><a href="#列表的排序过渡" class="headerlink" title="列表的排序过渡"></a>列表的排序过渡</h3><p><code>&lt;transition-group&gt;</code> 组件还有一个特殊之处。不仅可以进入和离开动画，<strong>还可以改变定位</strong>。要使用这个新功能只需了解新增的 <code>v-move</code> 特性，<strong>它会在元素的改变定位的过程中应用</strong>。</p><ul><li><code>v-move</code> 和 <code>v-leave-active</code> 结合使用，能够让列表的过渡更加平缓柔和：</li></ul><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs css"><span class="hljs-selector-class">.v-move</span>&#123;<br>  <span class="hljs-attribute">transition</span>: all <span class="hljs-number">0.8s</span> ease;<br>&#125;<br><span class="hljs-selector-class">.v-leave-active</span>&#123;<br>  <span class="hljs-attribute">position</span>: absolute;<br>&#125;<br></code></pre></td></tr></table></figure><h2 id="定义-Vue-组件"><a href="#定义-Vue-组件" class="headerlink" title="定义 Vue 组件"></a>定义 Vue 组件</h2><p>什么是组件： 组件的出现，就是为了拆分 Vue 实例的代码量的，能够让我们以不同的组件，来划分不同的功能模块，将来我们需要什么样的功能，就可以去调用对应的组件即可；<br>组件化和模块化的不同：</p><ul><li>模块化： 是从代码逻辑的角度进行划分的；方便代码分层开发，保证每个功能模块的职能单一；</li><li>组件化： 是从 UI 界面的角度进行划分的；前端的组件化，方便 UI 组件的重用；</li></ul><h3 id="全局组件定义的三种方式"><a href="#全局组件定义的三种方式" class="headerlink" title="全局组件定义的三种方式"></a>全局组件定义的三种方式</h3><ol><li>使用 Vue.extend 配合 Vue.component 方法：</li></ol><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">var <span class="hljs-keyword">login</span> = Vue.extend(&#123;<br>      <span class="hljs-keyword">template</span>: <span class="hljs-string">&#x27;&lt;h1&gt;登录&lt;/h1&gt;&#x27;</span><br>    &#125;);<br>    Vue.component(<span class="hljs-string">&#x27;login&#x27;</span>, <span class="hljs-keyword">login</span>);<br></code></pre></td></tr></table></figure><ol><li>直接使用 Vue.component 方法：</li></ol><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">Vue.component(<span class="hljs-string">&#x27;register&#x27;</span>, &#123;<br>      <span class="hljs-keyword">template</span>: <span class="hljs-string">&#x27;&lt;h1&gt;注册&lt;/h1&gt;&#x27;</span><br>    &#125;);<br></code></pre></td></tr></table></figure><ol><li>将模板字符串，定义到 script 标签种：</li></ol><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;tmpl&quot;</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;x-template&quot;</span>&gt;</span><span class="handlebars"><span class="xml"></span></span><br><span class="xml"><span class="handlebars">      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;#&quot;</span>&gt;</span>登录<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span> | <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;#&quot;</span>&gt;</span>注册<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span></span><br><span class="xml"><span class="handlebars">    </span></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br></code></pre></td></tr></table></figure><p>同时，需要使用 Vue.component 来定义组件：</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">Vue.component(<span class="hljs-string">&#x27;account&#x27;</span>, &#123;<br>      <span class="hljs-keyword">template</span>: <span class="hljs-string">&#x27;#tmpl&#x27;</span><br>    &#125;);<br></code></pre></td></tr></table></figure><blockquote><p>注意： 组件中的 DOM 结构，有且只能有唯一的根元素（Root Element）来进行包裹！</p></blockquote><h3 id="组件中展示数据和响应事件"><a href="#组件中展示数据和响应事件" class="headerlink" title="组件中展示数据和响应事件"></a>组件中展示数据和响应事件</h3><ol><li>在组件中，<code>data</code>需要被定义为一个方法，例如：</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs javascript">Vue.component(<span class="hljs-string">&#x27;account&#x27;</span>, &#123;<br>      <span class="hljs-attr">template</span>: <span class="hljs-string">&#x27;#tmpl&#x27;</span>,<br>      <span class="hljs-function"><span class="hljs-title">data</span>(<span class="hljs-params"></span>)</span> &#123;<br>        <span class="hljs-keyword">return</span> &#123;<br>          <span class="hljs-attr">msg</span>: <span class="hljs-string">&#x27;大家好！&#x27;</span><br>        &#125;<br>      &#125;,<br>      <span class="hljs-attr">methods</span>:&#123;<br>        <span class="hljs-function"><span class="hljs-title">login</span>(<span class="hljs-params"></span>)</span>&#123;<br>          alert(<span class="hljs-string">&#x27;点击了登录按钮&#x27;</span>);<br>        &#125;<br>      &#125;<br>    &#125;);<br></code></pre></td></tr></table></figure><ol><li>在子组件中，如果将模板字符串，定义到了 script 标签中，那么，要访问子组件身上的<code>data</code>属性中的值，需要使用<code>this</code>来访问；</li></ol><ul><li>组件可以有自己的 data 数据</li><li>组件的 data 和 实例的 data 有点不一样,实例中的 data 可以为一个对象,但是 组件中的 data 必须是一个方法</li><li>组件中的 data 除了必须为一个方法之外,这个方法内部,还必须返回一个对象才行;</li><li>组件中 的 data 数据,使用方式,和实例中的 data 使用方式完全一样!!!</li></ul><h2 id="组件切换"><a href="#组件切换" class="headerlink" title="组件切换"></a>组件切换</h2><p>vue 提供了 component，来展示对应的名称组件<br>component 是一个占位符, :is 属性,可以用来指定要展示的组件的名称</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;app&quot;</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;&quot;</span> @<span class="hljs-attr">click.prevent</span>=<span class="hljs-string">&quot;componentId=&#x27;login&#x27;&quot;</span>&gt;</span>登录<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;&quot;</span> @<span class="hljs-attr">click.prevent</span>=<span class="hljs-string">&quot;componentId=&#x27;register&#x27;&quot;</span>&gt;</span>注册<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">component</span> <span class="hljs-attr">:is</span>=<span class="hljs-string">&quot;componentId&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">component</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br></code></pre></td></tr></table></figure><p>当前学习了几个 Vue 提供的标签：</p><blockquote><p>component, template, transition, transitionGroup</p></blockquote><h2 id="父组件向子组件传值"><a href="#父组件向子组件传值" class="headerlink" title="父组件向子组件传值"></a>父组件向子组件传值</h2><ol><li>组件实例定义方式，注意：一定要使用<code>props</code>属性来定义父组件传递过来的数据</li></ol><figure class="highlight handlebars"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs handlebars"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript"></span></span><br><span class="javascript"><span class="xml">    <span class="hljs-comment">// 创建 Vue 实例，得到 ViewModel</span></span></span><br><span class="javascript"><span class="xml">    <span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue(&#123;</span></span><br><span class="javascript"><span class="xml">      <span class="hljs-attr">el</span>: <span class="hljs-string">&#x27;#app&#x27;</span>,</span></span><br><span class="javascript"><span class="xml">      <span class="hljs-attr">data</span>: &#123;</span></span><br><span class="javascript"><span class="xml">        <span class="hljs-attr">msg</span>: <span class="hljs-string">&#x27;这是父组件中的消息&#x27;</span></span></span><br><span class="javascript"><span class="xml">      &#125;,</span></span><br><span class="javascript"><span class="xml">      <span class="hljs-attr">components</span>: &#123;</span></span><br><span class="javascript"><span class="xml">        <span class="hljs-attr">son</span>: &#123;</span></span><br><span class="javascript"><span class="xml">          <span class="hljs-attr">template</span>: <span class="hljs-string">&#x27;&lt;h1&gt;这是子组件 --- </span></span></span><span class="hljs-template-variable">&#123;&#123;<span class="hljs-name">finfo</span>&#125;&#125;</span><span class="xml"><span class="handlebars"><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>&#x27;,</span></span></span><br><span class="xml"><span class="handlebars"><span class="xml">          props: [&#x27;finfo&#x27;]</span></span></span><br><span class="xml"><span class="handlebars"><span class="xml">        &#125;</span></span></span><br><span class="xml"><span class="handlebars"><span class="xml">      &#125;</span></span></span><br><span class="xml"><span class="handlebars"><span class="xml">    &#125;);</span></span></span><br><span class="xml"><span class="handlebars"><span class="xml">  </span></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span><br></code></pre></td></tr></table></figure><ol><li>使用<code>v-bind</code>或简化指令，将数据传递到子组件中：</li></ol><figure class="highlight elixir"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs elixir">&lt;div id=<span class="hljs-string">&quot;app&quot;</span>&gt;<br>    &lt;son <span class="hljs-symbol">:finfo=<span class="hljs-string">&quot;msg&quot;</span>&gt;&lt;/son&gt;</span><br>  &lt;<span class="hljs-regexp">/div&gt;</span><br></code></pre></td></tr></table></figure><h2 id="子组件向父组件传值"><a href="#子组件向父组件传值" class="headerlink" title="子组件向父组件传值"></a>子组件向父组件传值</h2><ol><li>原理：父组件将方法的引用，传递到子组件内部，子组件在内部调用父组件传递过来的方法，同时把要发送给父组件的数据，在调用方法的时候当作参数传递进去；</li><li>父组件将方法的引用传递给子组件，其中，<code>getMsg</code>是父组件中<code>methods</code>中定义的方法名称，<code>func</code>是子组件调用传递过来方法时候的方法名称</li></ol><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">son</span> @<span class="hljs-attr">func</span>=<span class="hljs-string">&quot;getMsg&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">son</span>&gt;</span><br></code></pre></td></tr></table></figure><ol><li>子组件内部通过<code>this.$emit(&#39;方法名&#39;, 要传递的数据)</code>方式，来调用父组件中的方法，同时把数据传递给父组件使用</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs js">&lt;div id=<span class="hljs-string">&quot;app&quot;</span>&gt;<br>    &lt;!-- 引用父组件 --&gt;<br>    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">son</span> @<span class="hljs-attr">func</span>=<span class="hljs-string">&quot;getMsg&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">son</span>&gt;</span></span><br><br>    &lt;!-- 组件模板定义 --&gt;<br>    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;x-template&quot;</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;son&quot;</span>&gt;</span><span class="handlebars"><span class="xml"></span></span></span><br><span class="xml"><span class="handlebars"><span class="xml">      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span></span></span></span><br><span class="xml"><span class="handlebars"><span class="xml">        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">&quot;button&quot;</span> <span class="hljs-attr">value</span>=<span class="hljs-string">&quot;向父组件传值&quot;</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">&quot;sendMsg&quot;</span> /&gt;</span></span></span></span><br><span class="xml"><span class="handlebars"><span class="xml">      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span></span></span><br><span class="xml"><span class="handlebars"><span class="xml">    </span></span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span><br>  &lt;/div&gt;<br><br>  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript"></span></span><br><span class="javascript"><span class="xml">    <span class="hljs-comment">// 子组件的定义方式</span></span></span><br><span class="javascript"><span class="xml">    Vue.component(<span class="hljs-string">&#x27;son&#x27;</span>, &#123;</span></span><br><span class="javascript"><span class="xml">      <span class="hljs-attr">template</span>: <span class="hljs-string">&#x27;#son&#x27;</span>, <span class="hljs-comment">// 组件模板Id</span></span></span><br><span class="javascript"><span class="xml">      <span class="hljs-attr">methods</span>: &#123;</span></span><br><span class="javascript"><span class="xml">        <span class="hljs-function"><span class="hljs-title">sendMsg</span>(<span class="hljs-params"></span>)</span> &#123; <span class="hljs-comment">// 按钮的点击事件</span></span></span><br><span class="javascript"><span class="xml">          <span class="hljs-built_in">this</span>.$emit(<span class="hljs-string">&#x27;func&#x27;</span>, <span class="hljs-string">&#x27;OK&#x27;</span>); <span class="hljs-comment">// 调用父组件传递过来的方法，同时把数据传递出去</span></span></span><br><span class="javascript"><span class="xml">        &#125;</span></span><br><span class="javascript"><span class="xml">      &#125;</span></span><br><span class="javascript"><span class="xml">    &#125;);</span></span><br><span class="javascript"><span class="xml"></span></span><br><span class="javascript"><span class="xml">    <span class="hljs-comment">// 创建 Vue 实例，得到 ViewModel</span></span></span><br><span class="javascript"><span class="xml">    <span class="hljs-keyword">var</span> vm = <span class="hljs-keyword">new</span> Vue(&#123;</span></span><br><span class="javascript"><span class="xml">      <span class="hljs-attr">el</span>: <span class="hljs-string">&#x27;#app&#x27;</span>,</span></span><br><span class="javascript"><span class="xml">      <span class="hljs-attr">data</span>: &#123;&#125;,</span></span><br><span class="javascript"><span class="xml">      <span class="hljs-attr">methods</span>: &#123;</span></span><br><span class="javascript"><span class="xml">        <span class="hljs-function"><span class="hljs-title">getMsg</span>(<span class="hljs-params">val</span>)</span>&#123; <span class="hljs-comment">// 子组件中，通过 this.$emit() 实际调用的方法，在此进行定义</span></span></span><br><span class="javascript"><span class="xml">          alert(val);</span></span><br><span class="javascript"><span class="xml">        &#125;</span></span><br><span class="javascript"><span class="xml">      &#125;</span></span><br><span class="javascript"><span class="xml">    &#125;);</span></span><br><span class="javascript"><span class="xml">  </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span><br></code></pre></td></tr></table></figure><h2 id="vue-router"><a href="#vue-router" class="headerlink" title="vue-router"></a>vue-router</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> VueRouter(&#123;<br>  <span class="hljs-attr">routes</span>: [<br>    &#123;<br>      <span class="hljs-attr">path</span>: <span class="hljs-string">&quot;/&quot;</span>,<br>      <span class="hljs-attr">redirect</span>: <span class="hljs-string">&quot;/home&quot;</span>,<br>      <span class="hljs-attr">meta</span>: &#123;<br>        <span class="hljs-attr">title</span>: <span class="hljs-string">&quot;首页&quot;</span><br>      &#125;<br>    &#125;,<br>  ],<br>  <span class="hljs-attr">mode</span>: <span class="hljs-string">&quot;history&quot;</span>,<br>  <span class="hljs-attr">linkActiveClass</span>: <span class="hljs-string">&quot;active&quot;</span><br>&#125;);<br><br><span class="hljs-comment">// 前置守卫</span><br>router.beforeEach(<span class="hljs-function">(<span class="hljs-params">to, <span class="hljs-keyword">from</span>, next</span>) =&gt;</span> &#123;<br>  <span class="hljs-comment">// 从from到to</span><br>  <span class="hljs-built_in">document</span>.title = to.matched[<span class="hljs-number">0</span>].meta.title || <span class="hljs-string">&quot;vuebox&quot;</span>;<br>  next();<br>&#125;);<br><span class="hljs-comment">// 后置钩子</span><br>router.afterEach(<span class="hljs-function">(<span class="hljs-params">to, <span class="hljs-keyword">from</span></span>) =&gt;</span> &#123;<br>  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">&quot;----------&quot;</span>);<br>&#125;);<br></code></pre></td></tr></table></figure><h3 id="keep-alive"><a href="#keep-alive" class="headerlink" title="keep-alive"></a>keep-alive</h3><p>activated和deactivated只有该组件使用了keep-alive时才是有效的。</p><p>组件内路由，<code>beforeRouteLeave(to,from,next)&#123;&#125;</code></p><p><code>include</code> 和<code>exclude</code></p><h2 id="插槽slot"><a href="#插槽slot" class="headerlink" title="插槽slot"></a>插槽slot</h2><p><code>vue2.x</code></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">slot</span>=<span class="hljs-string">&quot;item-icon&quot;</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>前置图标<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span><br></code></pre></td></tr></table></figure><p><code>vue3.x</code></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-slot:pre-icon</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span><br>        前置图标<br><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span><br></code></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-built_in">this</span>.$router.push(<span class="hljs-string">&#x27;/home/&#x27;</span>+<span class="hljs-number">123</span>)   <br><span class="hljs-built_in">this</span>.$router.push(&#123;<span class="hljs-attr">path</span>:<span class="hljs-string">&#x27;/home&#x27;</span>,<span class="hljs-attr">query</span>:&#123;<span class="hljs-attr">id</span>:<span class="hljs-number">123</span>&#125;&#125;)<br></code></pre></td></tr></table></figure>]]></content>
    
    
    <categories>
      
      <category>编程</category>
      
      <category>更新中</category>
      
    </categories>
    
    
    <tags>
      
      <tag>Vue</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>重构2——改善既有代码的设计</title>
    <link href="/blog/posts/b189f81a.html"/>
    <url>/blog/posts/b189f81a.html</url>
    
    <content type="html"><![CDATA[<img src="/blog/posts/b189f81a/005Ouxuxgy1g1omsp9johj33281j4u0x.jpg" class=""><span id="more"></span><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><div class="note info">都能写出计算机可以理解代码，唯有写出人类容易理解的代码的，才是优秀的程序员。<p>    好代码的标准就是人们能否轻而易举地修改它    </p></div><h1 id="重构的原则"><a href="#重构的原则" class="headerlink" title="重构的原则"></a>重构的原则</h1><h2 id="何谓重构"><a href="#何谓重构" class="headerlink" title="何谓重构"></a>何谓重构</h2><p>重构这个词既可以是名词也可以是动词，名词形式的定义的是：</p><div class="note info">对软件内部结构的调整，目的是在不改变软件可观察行为的前提下，提高其可理解性，降低其修改成本。</div>动词形式的定义的是：<div class="note success">使用一系列的重构手法，在不改变可观察行为的前提下，调整其结构</div><h2 id="为何重构"><a href="#为何重构" class="headerlink" title="为何重构"></a>为何重构</h2><ul><li>重构改进软件的设计</li><li>重构时软件更容易理解</li><li>重构帮助找到bug</li><li>重构提高编程速度</li></ul><h2 id="何时重构"><a href="#何时重构" class="headerlink" title="何时重构"></a>何时重构</h2><ul><li>预备性重构：让添加的新功能更容易</li><li>帮助理解的重构：使代码更容易理解</li><li>捡垃圾式重构</li><li>有计划的重构和见机行事的重构</li><li>长期重构</li><li>复审代码时重构</li></ul><h2 id="何时不重构"><a href="#何时不重构" class="headerlink" title="何时不重构"></a>何时不重构</h2><ul><li>重写比重构还容易</li></ul><p>只有需要理解其工作原理时，对其重构才有价值。</p>]]></content>
    
    
    <categories>
      
      <category>技术</category>
      
      <category>更新中</category>
      
    </categories>
    
    
    <tags>
      
      <tag>总结</tag>
      
      <tag>摘抄</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>VSCode中python代码输出中文乱码解决方法</title>
    <link href="/blog/posts/87ed0839.html"/>
    <url>/blog/posts/87ed0839.html</url>
    
    <content type="html"><![CDATA[<h2 id="在-vscode-中编写-python-代码，输出中文时，控制台输出为乱码解决方法："><a href="#在-vscode-中编写-python-代码，输出中文时，控制台输出为乱码解决方法：" class="headerlink" title="在 vscode 中编写 python 代码，输出中文时，控制台输出为乱码解决方法："></a>在 vscode 中编写 python 代码，输出中文时，控制台输出为乱码解决方法：</h2><img src="/blog/posts/87ed0839/1568254353802.png" class="" width="1568254353802"><span id="more"></span><h3 id="先检查右下角编码集设置是否正确"><a href="#先检查右下角编码集设置是否正确" class="headerlink" title="先检查右下角编码集设置是否正确"></a>先检查右下角编码集设置是否正确</h3><img src="/blog/posts/87ed0839/1568254487426.png" class="" width="1568254487426"><h3 id="修改完后运行仍不行，可以在”文件”－”首选项”－”用户设置”中搜索-code-runner-executorMap-选项，提示需要在-setting-json-中修改"><a href="#修改完后运行仍不行，可以在”文件”－”首选项”－”用户设置”中搜索-code-runner-executorMap-选项，提示需要在-setting-json-中修改" class="headerlink" title="修改完后运行仍不行，可以在”文件”－”首选项”－”用户设置”中搜索 code-runner.executorMap 选项，提示需要在 setting.json 中修改"></a>修改完后运行仍不行，可以在”文件”－”首选项”－”用户设置”中搜索 code-runner.executorMap 选项，提示需要在 setting.json 中修改</h3><img src="/blog/posts/87ed0839/1568254594172.png" class="" width="1568254594172"><p>在 json 中添加下列属性</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-string">&quot;code-runner.executorMap&quot;</span>: &#123;<br><span class="hljs-attr">&quot;python&quot;</span>: <span class="hljs-string">&quot;set PYTHONIOENCODING=utf8 &amp;&amp; python -u&quot;</span><br>&#125;<br></code></pre></td></tr></table></figure><div class="note default">https://mp.weixin.qq.com/s/QIJ-QHkxZUyKyQAPG49vPg</div>]]></content>
    
    
    <categories>
      
      <category>技术</category>
      
    </categories>
    
    
    <tags>
      
      <tag>Python</tag>
      
      <tag>Vscode</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>使用hexo过程中遇到的错误记录</title>
    <link href="/blog/posts/326cb881.html"/>
    <url>/blog/posts/326cb881.html</url>
    
    <content type="html"><![CDATA[<div class="note note-danger">            <p>遇到hexo编译之后发布不成功，报<code>Permission denied</code>的错!</p> <figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs sh">Permission denied (publickey).<br>fatal: Could not <span class="hljs-built_in">read</span> from remote repository.<br><br>Please make sure you have the correct access rights<br>and the repository exists.<br>FATAL Something<span class="hljs-string">&#x27;s wrong. Maybe you can find the solution here: http://hexo.io/docs/troubleshooting.html</span><br></code></pre></td></tr></table></figure> <p>解决方法：</p> <p>使用git bash解决了，会弹出密码输入框，填入就好了！</p> <p>参考文章：<a href="https://blog.csdn.net/dingding_12345/article/details/69666233">https://blog.csdn.net/dingding_12345/article/details/69666233</a></p> <p><img src="/blog/posts/326cb881/image-20200618100855896.png" class="" title="image-20200618100855896"></p> <p>又遇到<code>RPC failed; curl 56 OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 10054</code>的错：</p> <figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs sh">Error: fatal: the remote end hung up unexpectedly<br>fatal: the remote end hung up unexpectedly<br>error: RPC failed; curl 56 OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 10054<br></code></pre></td></tr></table></figure> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs Shell">git config http.postBuffer 524288000<br></code></pre></td></tr></table></figure> <p>在主题下的<code>_config.yml</code>文件中设置：</p> <h4 id="使用了-hexo-asset-image-和-hexo-abbrlink-后，图片显示不出来。"><a href="#使用了-hexo-asset-image-和-hexo-abbrlink-后，图片显示不出来。" class="headerlink" title="使用了 hexo-asset-image 和 hexo-abbrlink 后，图片显示不出来。"></a>使用了 hexo-asset-image 和 hexo-abbrlink 后，图片显示不出来。</h4><p>issue：<a href="https://github.com/rozbo/hexo-abbrlink/issues/19">https://github.com/rozbo/hexo-abbrlink/issues/19</a></p> <p>解决方法：<a href="https://github.com/foreveryang321/hexo-asset-image">https://github.com/foreveryang321/hexo-asset-image</a></p>           </div>]]></content>
    
    
    <categories>
      
      <category>技术</category>
      
    </categories>
    
    
    <tags>
      
      <tag>Hexo</tag>
      
      <tag>NexT</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>markdown的使用总结</title>
    <link href="/blog/posts/3c0dd009.html"/>
    <url>/blog/posts/3c0dd009.html</url>
    
    <content type="html"><![CDATA[<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs markdown">gitGraph:<br>options<br>&#123;<br><span class="hljs-code">    &quot;nodeSpacing&quot;: 150,</span><br><span class="hljs-code">    &quot;nodeRadius&quot;: 10</span><br><span class="hljs-code">&#125;</span><br><span class="hljs-code">end</span><br><span class="hljs-code">commit</span><br><span class="hljs-code">branch newbranch</span><br><span class="hljs-code">checkout newbranch</span><br><span class="hljs-code">commit</span><br><span class="hljs-code">commit</span><br><span class="hljs-code">checkout master</span><br><span class="hljs-code">commit</span><br><span class="hljs-code">commit</span><br><span class="hljs-code">merge newbranch</span><br></code></pre></td></tr></table></figure><pre><code class=" mermaid">gitGraph:options&#123;    &quot;nodeSpacing&quot;: 150,    &quot;nodeRadius&quot;: 10&#125;endcommitbranch newbranchcheckout newbranchcommitcommitcheckout mastercommitcommitmerge newbranch</code></pre><figure class="highlight subunit"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs subunit">gantt<br>        dateFormat  YYYY-MM-DD<br>        title Adding GANTT diagram functionality to mermaid<br>        section 现有任务<br>        已完成               :done,    des1, 2014<span class="hljs-string">-01</span><span class="hljs-string">-06</span>,2014<span class="hljs-string">-01</span><span class="hljs-string">-08</span><br>        进行中               :active,  des2, 2014<span class="hljs-string">-01</span><span class="hljs-string">-09</span>, 3d<br>        计划中               :         des3, after des2, 5d<br></code></pre></td></tr></table></figure><pre><code class=" mermaid">gantt        dateFormat  YYYY-MM-DD        title Adding GANTT diagram functionality to mermaid        section 现有任务        已完成               :done,    des1, 2014-01-06,2014-01-08        进行中               :active,  des2, 2014-01-09, 3d        计划中               :         des3, after des2, 5d</code></pre>]]></content>
    
    
    
  </entry>
  
  
  
  <entry>
    <title>使用Hexo + NexT 快速搭建博客</title>
    <link href="/blog/posts/eba3f111.html"/>
    <url>/blog/posts/eba3f111.html</url>
    
    <content type="html"><![CDATA[<img src="/blog/posts/eba3f111/pic1.png" class=""><span id="more"></span><h1 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h1><h2 id="安装-node-js"><a href="#安装-node-js" class="headerlink" title="安装 node.js"></a>安装 node.js</h2><div class="note note-success">            <p>如果你已经安装了 node.js，请忽略。</p>           </div><p>访问<a href="https://nodejs.org/en/">node.js 官网</a>，根据指引进行安装。</p><h2 id="安装-Git"><a href="#安装-Git" class="headerlink" title="安装 Git"></a>安装 Git</h2><div class="note note-success">            <p>如果你已经安装了 Git，请忽略。</p>           </div><p>访问<a href="https://git-scm.com/">Git 官网</a>，根据指引进行安装。</p><div class="note note-warning">            <p>由于众所周知的原因，Windows 从上面的链接下载 git for windows 最好挂上一个代理，否则下载速度十分缓慢。也可以参考<a href="https://github.com/waylau/git-for-win">这个页面</a>，收录了存储于百度云的下载地址。</p>           </div><h2 id="安装-Hexo"><a href="#安装-Hexo" class="headerlink" title="安装 Hexo"></a>安装 Hexo</h2><ul><li><p><strong>国内的朋友</strong>，因为众所周知的原因，从 npm 直接安装 hexo 会非常慢，所以你需要用到<a href="https://npm.taobao.org/"><strong>镜像源</strong></a>，参考上面的步骤，使用 cnpm 命令行工具代替默认的 npm: 在 windows 控制台（cmd）里输入并执行<code>npm install -g cnpm --registry=https://registry.npm.taobao.org</code>，然后安装 hexo: <code>cnpm install -g hexo-cli</code></p></li><li><p><strong>国外的朋友</strong>，请直接打开 windows 控制台，输入<code>npm install -g hexo-cli</code>并执行。</p></li></ul><hr><h1 id="建站"><a href="#建站" class="headerlink" title="建站"></a>建站</h1><h2 id="建立本地博客文件夹"><a href="#建立本地博客文件夹" class="headerlink" title="建立本地博客文件夹"></a>建立本地博客文件夹</h2><p>在命令行执行如下命令，其中<code>&lt;folder&gt;</code>为文件夹路径</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">hexo init &lt;folder&gt;<br><span class="hljs-built_in">cd</span> &lt;folder&gt;<br></code></pre></td></tr></table></figure><div class="note note-warning">            <p><strong>所有有关<code>hexo</code>的命令</strong> 均要在<code>&lt;folder&gt;</code>路径下执行。</p>           </div><p>建立好后文件夹目录如下</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs nohighlight">.<br>├── _config.yml<br>├── package.json<br>├── .gitignore<br>├── node_modules<br>├── scaffolds<br>├── source<br>|   ├── _posts<br>└── themes<br></code></pre></td></tr></table></figure><p>其中</p><ul><li><p><code>_config.yml</code>：站点的配置文件，可以在此配置大部分的参数。</p></li><li><p><code>package.json</code>：应用程序的信息。EJS, Stylus 和 Markdown renderer 已默认安装，您可以自由移除。</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs json">&#123;<br><span class="hljs-attr">&quot;name&quot;</span>: <span class="hljs-string">&quot;hexo-site&quot;</span>,<br><span class="hljs-attr">&quot;version&quot;</span>: <span class="hljs-string">&quot;0.0.0&quot;</span>,<br><span class="hljs-attr">&quot;private&quot;</span>: <span class="hljs-literal">true</span>,<br><span class="hljs-attr">&quot;hexo&quot;</span>: &#123;<br><span class="hljs-attr">&quot;version&quot;</span>: <span class="hljs-string">&quot;3.9.0&quot;</span><br>&#125;,<br><span class="hljs-attr">&quot;dependencies&quot;</span>: &#123;<br><span class="hljs-attr">&quot;version&quot;</span>: <span class="hljs-string">&quot;3.9.0&quot;</span><br><span class="hljs-string">&quot;hexo-generator-archive&quot;</span>: <span class="hljs-string">&quot;^0.1.5&quot;</span>,<br><span class="hljs-attr">&quot;hexo-generator-category&quot;</span>: <span class="hljs-string">&quot;^0.1.3&quot;</span>,<br><span class="hljs-attr">&quot;hexo-generator-index&quot;</span>: <span class="hljs-string">&quot;^0.2.1&quot;</span>,<br><span class="hljs-attr">&quot;hexo-generator-tag&quot;</span>: <span class="hljs-string">&quot;^0.2.0&quot;</span>,<br><span class="hljs-attr">&quot;hexo-renderer-ejs&quot;</span>: <span class="hljs-string">&quot;^0.3.1&quot;</span>,<br><span class="hljs-attr">&quot;hexo-renderer-stylus&quot;</span>: <span class="hljs-string">&quot;^0.3.3&quot;</span>,<br><span class="hljs-attr">&quot;hexo-renderer-marked&quot;</span>: <span class="hljs-string">&quot;^1.0.1&quot;</span>,<br><span class="hljs-attr">&quot;hexo-server&quot;</span>: <span class="hljs-string">&quot;^0.3.3&quot;</span><br>&#125;<br></code></pre></td></tr></table></figure></li><li><p>scaffolds：模板文件夹，是指在新建的文章文件中默认填充的内容。例如，如果您修改scaffold/post.md中的Front-matter内容，那么每次新建一篇文章时都会包含这个修改。</p></li><li><p>source：资源文件夹，存放用户资源的地方。除<code>_posts</code>文件夹之外，开头命名为 _ (下划线)的文件/文件夹和隐藏的文件将会被忽略。Markdown 和 HTML 文件会被解析并放到 public 文件夹，而其他文件会被拷贝过去。</p></li><li><p>themes：主题文件夹。Hexo 会根据主题来生成静态页面。</p></li><li><p>node_modules：node.js 模块，一些 <strong>插件</strong> 和 <strong>依赖</strong> 会被安装到这里。</p></li></ul><div class="note note-info">            <p>更加详细的解释请参考<a href="https://hexo.io/zh-cn/docs/">hexo 官方文档</a></p>           </div><h3 id="安装-NexT-主题"><a href="#安装-NexT-主题" class="headerlink" title="安装 NexT 主题"></a>安装 NexT 主题</h3><p>进入本地博客文件夹并将 NexT 主题<code>clone</code>至<code>themes</code>文件夹下</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git <span class="hljs-built_in">clone</span> https://github.com/theme-next/hexo-theme-next themes/next<br></code></pre></td></tr></table></figure><p>你会看到，在<code>next</code>下也有一个<code>_config.yml</code>的文件，这是 <strong>NexT 主题的配置文件</strong>，为了区别它和 <strong>博客配置文件</strong>，下面会用带路径的文件名来描述它们：</p><ul><li><code>&lt;folder&gt;/_config.yml</code>：站点配置文件</li><li><code>next/_config.yml</code>：主题配置文件</li></ul><h2 id="启用-NexT-主题"><a href="#启用-NexT-主题" class="headerlink" title="启用 NexT 主题"></a>启用 NexT 主题</h2><p>在<code>&lt;folder&gt;/_config.yml</code>里<code>theme:</code>选项填<code>next</code>，=&gt;<code>theme: next</code>，注意冒号后空一格。</p><p>到这里，建站的任务就完成了。你现在可以打开控制台，输入并执行如下命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">hexo g<br></code></pre></td></tr></table></figure><p>完成没有报错之后执行如下命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">hexo s<br></code></pre></td></tr></table></figure><p>其中</p><ul><li><code>hexo g</code>：新建<code>public</code>文件夹，并在其中生成网站静态文件（html，css，等文件）</li><li><code>hexo s</code>：启动 hexo 服务器，默认情况下，访问网址为：<code>http://localhost:4000/</code></li></ul><div class="note note-info">            <p>更多有关 hexo 的命令，请参考<a href="https://hexo.io/zh-cn/docs/">hexo 官方文档</a>的<a href="https://hexo.io/zh-cn/docs/commands.html">命令</a>部分。</p>           </div><p>你最后会看到控制台有如下输出：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">INFO  Hexo is running at http://localhost:4000/. Press Ctrl+C to stop.<br></code></pre></td></tr></table></figure><p>在浏览器地址栏输入<code>http://localhost:4000/</code>并访问，你应该会看到如下页面：<br><img src="/blog/posts/eba3f111/pic2.png" class=""></p><div class="note success"><i class="fa fa-thumbs-o-up" aria-hidden="true"></i>&nbsp;**恭喜你！你已经完成了博客搭建的主要工作！接下来就是细节的配置了。请耐心阅读以下内容。**</div><h1 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h1><h2 id="网站脚注"><a href="#网站脚注" class="headerlink" title="网站脚注"></a>网站脚注</h2><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">footer:</span><br>  <span class="hljs-comment">#建站时间</span><br>  <span class="hljs-attr">since:</span> <span class="hljs-number">2018</span><br>  <span class="hljs-comment">#作者头像并且是动画效果</span><br>  <span class="hljs-attr">icon:</span><br>    <span class="hljs-attr">name:</span> <span class="hljs-string">user</span><br>    <span class="hljs-attr">animated:</span> <span class="hljs-literal">true</span><br>    <span class="hljs-attr">color:</span> <span class="hljs-string">&quot;##66CDAA&quot;</span><br>  <span class="hljs-comment">#显示版权作者</span><br>  <span class="hljs-attr">copyright:</span> <span class="hljs-string">aigisss</span> <span class="hljs-string">爱即是诗</span><br>  <span class="hljs-comment">#不显示Hexo</span><br>  <span class="hljs-attr">powered:</span><br>    <span class="hljs-attr">enable:</span> <span class="hljs-literal">false</span><br>    <span class="hljs-attr">version:</span> <span class="hljs-literal">false</span><br>  <span class="hljs-comment">#不显示主题和版本</span><br>  <span class="hljs-attr">theme:</span><br>    <span class="hljs-attr">enable:</span> <span class="hljs-literal">false</span><br>    <span class="hljs-attr">version:</span> <span class="hljs-literal">false</span><br>  <span class="hljs-comment">#显示备案号</span><br>  <span class="hljs-attr">beian:</span><br>    <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br>    <span class="hljs-attr">icp:</span> <span class="hljs-string">赣ICP备18013338-1号</span><br></code></pre></td></tr></table></figure><h2 id="版权声明"><a href="#版权声明" class="headerlink" title="版权声明"></a>版权声明</h2><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">creative_commons:</span><br>  <span class="hljs-attr">license:</span> <span class="hljs-string">by-nc-sa</span><br>  <span class="hljs-attr">sidebar:</span> <span class="hljs-literal">false</span><br>  <span class="hljs-attr">post:</span> <span class="hljs-literal">true</span><br></code></pre></td></tr></table></figure><h2 id="代码块"><a href="#代码块" class="headerlink" title="代码块"></a>代码块</h2><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">codeblock:</span><br>  <span class="hljs-comment"># 自定义边框半径，默认是1</span><br>  <span class="hljs-comment"># 值越大弧度越大</span><br>  <span class="hljs-attr">border_radius:</span> <span class="hljs-number">6</span><br>  <span class="hljs-comment"># 右上角显示复制按钮</span><br>  <span class="hljs-attr">copy_button:</span><br>    <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br>    <span class="hljs-comment"># 显示复制结果</span><br>    <span class="hljs-attr">show_result:</span> <span class="hljs-literal">true</span><br></code></pre></td></tr></table></figure><h2 id="分享"><a href="#分享" class="headerlink" title="分享"></a>分享</h2><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">needmoreshare2:</span><br>  <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br>  <span class="hljs-attr">postbottom:</span><br>    <span class="hljs-comment">#文章底部</span><br>    <span class="hljs-attr">enable:</span> <span class="hljs-literal">false</span><br>    <span class="hljs-attr">options:</span><br>      <span class="hljs-attr">iconStyle:</span> <span class="hljs-string">box</span><br>      <span class="hljs-attr">boxForm:</span> <span class="hljs-string">horizontal</span><br>      <span class="hljs-attr">position:</span> <span class="hljs-string">bottomCenter</span><br>      <span class="hljs-attr">networks:</span> <span class="hljs-string">Weibo,Wechat,Douban,QQZone,Twitter,Facebook</span><br>      <span class="hljs-comment">#左下角悬浮按钮</span><br>  <span class="hljs-attr">float:</span><br>    <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br>    <span class="hljs-attr">options:</span><br>      <span class="hljs-attr">iconStyle:</span> <span class="hljs-string">box</span><br>      <span class="hljs-attr">boxForm:</span> <span class="hljs-string">horizontal</span><br>      <span class="hljs-attr">position:</span> <span class="hljs-string">middleRight</span><br>      <span class="hljs-attr">networks:</span> <span class="hljs-string">Weibo,Wechat,Douban,QQZone,Twitter,Facebook</span><br></code></pre></td></tr></table></figure><h2 id="访问次数"><a href="#访问次数" class="headerlink" title="访问次数"></a>访问次数</h2><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># busuanzi统计</span><br><span class="hljs-attr">busuanzi_count:</span><br>  <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br>  <span class="hljs-comment"># 总访客数</span><br>  <span class="hljs-attr">total_visitors:</span> <span class="hljs-literal">true</span><br>  <span class="hljs-attr">total_visitors_icon:</span> <span class="hljs-string">user</span><br>  <span class="hljs-comment"># 总浏览量</span><br>  <span class="hljs-attr">total_views:</span> <span class="hljs-literal">true</span><br>  <span class="hljs-attr">total_views_icon:</span> <span class="hljs-string">eye</span><br>  <span class="hljs-comment"># 文章浏览量</span><br>  <span class="hljs-attr">post_views:</span> <span class="hljs-literal">true</span><br>  <span class="hljs-attr">post_views_icon:</span> <span class="hljs-string">eye</span><br></code></pre></td></tr></table></figure><h2 id="顶部阅读进度条"><a href="#顶部阅读进度条" class="headerlink" title="顶部阅读进度条"></a>顶部阅读进度条</h2><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">reading_progress:</span><br>  <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br>  <span class="hljs-attr">color:</span> <span class="hljs-string">&quot;#37c6c0&quot;</span><br>  <span class="hljs-attr">height:</span> <span class="hljs-string">2px</span><br></code></pre></td></tr></table></figure><h2 id="加载动画"><a href="#加载动画" class="headerlink" title="加载动画"></a>加载动画</h2><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">motion:</span><br>  <span class="hljs-comment"># 启用</span><br>  <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br>  <span class="hljs-comment"># 异步加载</span><br>  <span class="hljs-attr">async:</span> <span class="hljs-literal">true</span><br>  <span class="hljs-attr">transition:</span><br>    <span class="hljs-comment"># Transition variants:</span><br>    <span class="hljs-comment"># fadeIn | fadeOut | flipXIn | flipXOut | flipYIn | flipYOut | flipBounceXIn | flipBounceXOut | flipBounceYIn | flipBounceYOut</span><br>    <span class="hljs-comment"># swoopIn | swoopOut | whirlIn | whirlOut | shrinkIn | shrinkOut | expandIn | expandOut</span><br>    <span class="hljs-comment"># bounceIn | bounceOut | bounceUpIn | bounceUpOut | bounceDownIn | bounceDownOut | bounceLeftIn | bounceLeftOut | bounceRightIn | bounceRightOut</span><br>    <span class="hljs-comment"># slideUpIn | slideUpOut | slideDownIn | slideDownOut | slideLeftIn | slideLeftOut | slideRightIn | slideRightOut</span><br>    <span class="hljs-comment"># slideUpBigIn | slideUpBigOut | slideDownBigIn | slideDownBigOut | slideLeftBigIn | slideLeftBigOut | slideRightBigIn | slideRightBigOut</span><br>    <span class="hljs-comment"># perspectiveUpIn | perspectiveUpOut | perspectiveDownIn | perspectiveDownOut | perspectiveLeftIn | perspectiveLeftOut | perspectiveRightIn | perspectiveRightOut</span><br>    <span class="hljs-comment"># 文章摘要动画</span><br>    <span class="hljs-attr">post_block:</span> <span class="hljs-string">bounceIn</span><br>    <span class="hljs-comment"># 加载各种页面动画（分类，关于，标签等等）</span><br>    <span class="hljs-attr">post_header:</span> <span class="hljs-string">fadeIn</span><br>    <span class="hljs-comment"># 文章详情动画</span><br>    <span class="hljs-attr">post_body:</span> <span class="hljs-string">fadeIn</span><br>    <span class="hljs-comment">#</span><br>    <span class="hljs-attr">coll_header:</span> <span class="hljs-string">fadeIn</span><br>    <span class="hljs-comment"># Only for Pisces | Gemini.</span><br>    <span class="hljs-comment"># 侧边栏（人物头像的那部分）</span><br>    <span class="hljs-attr">sidebar:</span> <span class="hljs-string">fadeIn</span><br></code></pre></td></tr></table></figure><h2 id="搜索功能"><a href="#搜索功能" class="headerlink" title="搜索功能"></a>搜索功能</h2><p><code>NexT</code>自带提供了两个搜索</p><ul><li><code>algolia_search</code></li><li><code>local_search</code></li></ul><p>其实这个<code>local_search</code>已经很好用了，配置<code>algolia_search</code>挺麻烦的，而且搜索功能也用的不多</p><p>毕竟有万能的<code>Ctrl + F</code></p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">local_search:</span><br>  <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br>  <span class="hljs-comment"># if auto, trigger search by changing input</span><br>  <span class="hljs-comment"># if manual, trigger search by pressing enter key or search button</span><br>  <span class="hljs-attr">trigger:</span> <span class="hljs-string">auto</span><br>  <span class="hljs-comment"># show top n results per article, show all results by setting to -1</span><br>  <span class="hljs-attr">top_n_per_article:</span> <span class="hljs-number">1</span><br>  <span class="hljs-comment"># unescape html strings to the readable one</span><br>  <span class="hljs-attr">unescape:</span> <span class="hljs-literal">false</span><br></code></pre></td></tr></table></figure><h2 id="添加-RSS-订阅"><a href="#添加-RSS-订阅" class="headerlink" title="添加 RSS 订阅"></a>添加 RSS 订阅</h2><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-string">npm</span> <span class="hljs-string">install</span> <span class="hljs-string">hexo-generator-feed</span> <span class="hljs-string">--save</span><br><br><span class="hljs-string">复制代码</span><br><span class="hljs-comment"># Extensions</span><br><span class="hljs-attr">plugins:</span><br>   <span class="hljs-string">hexo-generate-feed</span><br><span class="hljs-attr">feed:</span> <span class="hljs-comment"># RSS订阅插件</span><br>  <span class="hljs-attr">type:</span> <span class="hljs-string">atom</span><br>  <span class="hljs-attr">path:</span> <span class="hljs-string">atom.xml</span><br>  <span class="hljs-attr">limit:</span> <span class="hljs-number">0</span> <span class="hljs-comment">#0就是代表所有</span><br></code></pre></td></tr></table></figure><h2 id="数学公式"><a href="#数学公式" class="headerlink" title="数学公式"></a>数学公式</h2><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># Math Equations Render Support</span><br><span class="hljs-attr">math:</span><br>  <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br><br>  <span class="hljs-comment"># Default(true) will load mathjax/katex script on demand</span><br>  <span class="hljs-comment"># That is it only render those page who has &#x27;mathjax: true&#x27; in Front Matter.</span><br>  <span class="hljs-comment"># If you set it to false, it will load mathjax/katex srcipt EVERY PAGE.</span><br>  <span class="hljs-attr">per_page:</span> <span class="hljs-literal">true</span><br><br>  <span class="hljs-attr">engine:</span> <span class="hljs-string">mathjax</span><br>  <span class="hljs-comment">#engine: katex</span><br></code></pre></td></tr></table></figure><p>还需要在文章的 Front-matter 里打开 mathjax 开关，比如：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">title:</span> <span class="hljs-string">使用hexo下next主题搭建博客的记录</span><br><span class="hljs-attr">tags:</span> <span class="hljs-string">日常</span><br><span class="hljs-attr">abbrlink:</span> <span class="hljs-string">326cb881</span><br><span class="hljs-attr">date:</span> <span class="hljs-number">2019-09-09 09:58:21</span><br><span class="hljs-attr">mathjax:</span> <span class="hljs-literal">true</span><br></code></pre></td></tr></table></figure><blockquote><p>网上一大堆说会出现语义冲突——-类 Latex 格式书写的数学公式下划线<code>_</code>表示下标，有特殊的含义，如果被强制转换为<code>&lt;em&gt;</code>标签，那么 MathJax 引擎在渲染数学公式的时候就会出错。类似的语义冲突的符号还包括<code>*</code>, <code>&#123;</code>, <code>&#125;</code>, <code>\\</code>等。但是！！</p></blockquote><div class="note note-danger">            <p>在我试验下没有出现此类问题，只要在主题中打开，md 中申明 mathjax: true 就好了，可能在我使用的<code>next6.7</code>中解决了冲突。比如以下的公式能出来！</p>           </div><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs latex">\Gamma(z) = \int_0^\infty t^&#123;z-1&#125;e^&#123;-t&#125;dt\,.<br></code></pre></td></tr></table></figure><script type="math/tex; mode=display">\Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,.</script><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs latex">$$<br>P = \frac<br>&#123;\sum_&#123;i=1&#125;^n (x_i- x)(y_i- y)&#125;<br>&#123;\displaystyle \left[<br>\sum_&#123;i=1&#125;^n (x_i-x)^2<br>\sum_&#123;i=1&#125;^n (y_i-y)^2<br>\right]^&#123;1/2&#125; &#125;<br>$$<br></code></pre></td></tr></table></figure><script type="math/tex; mode=display">P = \frac{\sum_{i=1}^n (x_i- x)(y_i- y)}{\displaystyle \left[\sum_{i=1}^n (x_i-x)^2\sum_{i=1}^n (y_i-y)^2\right]^{1/2} }</script><h2 id="添加自定义菜单"><a href="#添加自定义菜单" class="headerlink" title="添加自定义菜单"></a>添加自定义菜单</h2><p>以新建「相册」菜单为例：在博客目录下的 source 文件夹下新建名为 photo 文件夹，然后在 photo 文件夹下新建一个 index.md 文件，然后在该文件填写：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><br><span class="hljs-attr">title:</span> <span class="hljs-string">相册</span><br><span class="hljs-attr">date:</span> <span class="hljs-number">2018-04-16 22:14:07</span><br><span class="hljs-attr">type:</span> <span class="hljs-string">&quot;photo&quot;</span><br><span class="hljs-meta">---</span><br></code></pre></td></tr></table></figure><p>然后打开主题配置文件 <code>_config.yml</code>，在 menu 中添加：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">menu:</span><br>  <span class="hljs-attr">home:</span> <span class="hljs-string">/</span> <span class="hljs-string">||</span> <span class="hljs-string">home</span><br>  <span class="hljs-attr">archives:</span> <span class="hljs-string">/archives</span> <span class="hljs-string">||</span> <span class="hljs-string">archive</span><br>  <span class="hljs-attr">categories:</span> <span class="hljs-string">/categories</span> <span class="hljs-string">||</span> <span class="hljs-string">th</span><br>  <span class="hljs-attr">tags:</span> <span class="hljs-string">/tags</span> <span class="hljs-string">||</span> <span class="hljs-string">tags</span><br>  <span class="hljs-comment">#添加「相册」菜单</span><br>  <span class="hljs-string">相册:</span> <span class="hljs-string">/photo</span> <span class="hljs-string">||</span> <span class="hljs-string">camera</span><br></code></pre></td></tr></table></figure><blockquote><p>这里的「相册」是博客中显示的菜单名称，紧跟的 photo 要和前面 index.md 文件的 type 值一致，|| 后面的菜单的图标，图标名称来自于 FontAwesome icon，若没有配置图标，默认会使用问号图标         </p></blockquote><h2 id="修改文章底部的那个带-号的标签"><a href="#修改文章底部的那个带-号的标签" class="headerlink" title="修改文章底部的那个带#号的标签"></a>修改文章底部的那个带#号的标签</h2><p>修改模板<code>/themes/next/layout/_macro/post.swig</code>，搜索 <code>rel=&quot;tag&quot;&gt;#</code>，将 # 换成<code>&lt;i class=&quot;fa fa-tag&quot;&gt;&lt;/i&gt;</code></p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><blockquote><ol><li><strong><em><a href="https://juejin.im/post/5bcd2d395188255c3b7dc1db">Hexo+NexT 打造一个炫酷博客</a></em></strong></li><li><strong><em><a href="https://juejin.im/post/5a71ab9f518825735300ee6c">篇Ⅱ：NexT主题的配置和优化指南</a></em></strong></li></ol></blockquote>]]></content>
    
    
    <categories>
      
      <category>技术</category>
      
    </categories>
    
    
    <tags>
      
      <tag>Hexo</tag>
      
      <tag>NexT</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>git的操作记录</title>
    <link href="/blog/posts/d5d49586.html"/>
    <url>/blog/posts/d5d49586.html</url>
    
    <content type="html"><![CDATA[<h3 id="与服务器上的代码产生冲突"><a href="#与服务器上的代码产生冲突" class="headerlink" title="与服务器上的代码产生冲突"></a>与服务器上的代码产生冲突</h3><p>如果系统中有一些配置文件在服务器上做了配置修改,然后后续开发又新添加一些配置项的时候，在发布这个配置文件的时候,会发生代码冲突:<br><div class="note note-danger">            <p>error: Your local changes to the following files would be overwritten by merge:<br>protected/config/main.php<br>Please, commit your changes or stash them before you can merge.</p>           </div></p><p>如果希望保留生产服务器上所做的改动,仅仅并入新配置项, 处理方法如下:</p><span id="more"></span><figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs avrasm">git stash<br>git pull<br>git stash <span class="hljs-keyword">pop</span><br></code></pre></td></tr></table></figure><p>然后可以使用 <code>git diff -w +文件名</code>来确认代码自动合并的情况.</p><p>反过来,如果希望用代码库中的文件完全覆盖本地工作版本. 方法如下:</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">git <span class="hljs-keyword">reset</span> <span class="hljs-comment">--hard</span><br>git pull<br></code></pre></td></tr></table></figure><p>其中 <code>git reset</code> 是针对版本,如果想针对文件回退本地修改,使用</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git checkout HEAD file/to/restore<br></code></pre></td></tr></table></figure><h3 id="辛辛苦苦加班一星期敲的代码没了"><a href="#辛辛苦苦加班一星期敲的代码没了" class="headerlink" title="辛辛苦苦加班一星期敲的代码没了"></a>辛辛苦苦加班一星期敲的代码没了</h3><p>过程是这样的，在终端输入 git log，列出所有的 commit 信息，如下图：</p><img src="/blog/posts/d5d49586/1568253175476.png" class="" width="1568253175476"><p>commit 的信息很简单，就是做了 6 个功能，每个功能对应一个 commit 的提交，分别是 feature-1 到 feature-6。</p><p>接下来执行了强制回滚，如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git reset --hard 2216d4e<br></code></pre></td></tr></table></figure><p>回滚到了 feature-1 上，并且回滚的时候加了—hard，导致之前 feature-2 到 feature-6 的所有代码全部弄丢了，现在 git log 的显示如下：</p><img src="/blog/posts/d5d49586/1568253317626.png" class="" width="1568253317626"><p>现在 feature-2 到 feature-6 的代码没了。。。。。</p><p>然鹅还没完，在这个基础上新添加了一个 commit 提交，信息叫 feature-7，如下图：</p><img src="/blog/posts/d5d49586/1568253390009.png" class="" width="1568253390009"><p>现在 feature-2 到 feature-6 全没了，还多了一个 feature-7</p><div class="note note-info">            <p>请问 如何把丢失的代码 feature-2 到 feature-6 全部恢复回来，并且 feature-7 的代码也要保留</p>           </div><p><strong>用 git reflog 和 git cherry-pick 就能解决</strong></p><p>在终端里输入：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git reflog<br></code></pre></td></tr></table></figure><p>然后就会展示出所有你之前 git 操作，你以前所有的操作都被 git 记录了下来，如下图：</p><img src="/blog/posts/d5d49586/1568296363901.png" class="" width="1568296363901"><p>这时候要记好两个值：4c97ff3 和 cd52afc，他们分别是 feature-7 和 feature-6 的 hash 码。然后执行回滚，回到 feature-6 上：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git reset --hard cd52afc<br></code></pre></td></tr></table></figure><p>现在我们回到了 feature-6 上，如下图：</p><img src="/blog/posts/d5d49586/1568296459828.png" class="" width="1568296459828"><p>我们回到了 feature-6 上，但是 feature-7 没了，如何加上来呢？</p><p>这个时候就用上了 git cherry-pick，刚刚我们知道了 feature-7 的 hash 码为 4c97ff3，操作如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">git cherry-pick 4c97ff3<br></code></pre></td></tr></table></figure><p>回车之后，你的 feature-7 的代码就回来了。</p><p>期间可能会有一些冲突，按照提示解决就好。最后的结果如下图：</p><img src="/blog/posts/d5d49586/169d3f52baa26b7e.png" class=""><p>feature-1 到 feature-7 的代码就合并到了一起，以前的代码也都回来了。</p><h2 id="自己总结"><a href="#自己总结" class="headerlink" title="自己总结"></a>自己总结</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">git remote add origin https://github.com/cenergy/test.git<br>git push -u origin master<br></code></pre></td></tr></table></figure><p><a href="https://juejin.im/post/5cbd82165188250a926108bd?utm_source=gold_browser_extension">原文出自前端时光机</a></p>]]></content>
    
    
    <categories>
      
      <category>编程</category>
      
      <category>更新中</category>
      
    </categories>
    
    
    <tags>
      
      <tag>Git</tag>
      
    </tags>
    
  </entry>
  
  
  
  <entry>
    <title>使用coding-pages时出现静态文件丢失</title>
    <link href="/blog/posts/372cd070.html"/>
    <url>/blog/posts/372cd070.html</url>
    
    <content type="html"><![CDATA[<img src="/blog/posts/372cd070/1567646753123.png" class=""><span id="more"></span><p>出现这种情况，发现静态资源都加载不到，后来网上搜索了半天，才发现原来你要打算用 coding 的 pages 服务部署你的博客的话，你创建项目的名字必须和用户名保持一致，不能自己随便自定义。我重新创建了一个和用户名一致的项目，部署到他的 pages 服务，访问正常</p>]]></content>
    
    
    <categories>
      
      <category>技术</category>
      
    </categories>
    
    
    <tags>
      
      <tag>Hexo</tag>
      
      <tag>NexT</tag>
      
    </tags>
    
  </entry>
  
  
  
  
</search>
