<div itemprop="articleBody" data-v-49c8f584="" style="font-weight: 400;"><h1 data-id="heading-0">本文简介</h1>
<p>【今天我必须发一个封面!放文末!】</p>
<p>最近有工友问我前端怎么给图片做标注。使用 <code>Fabric.js</code> 或者 <code>Konva.js</code> 等库确实可以实现,但多少觉得有点大炮打蚊的感觉,好奇有没有专门做图片标注的工具呢?</p>
<p>在网上搜了一下发现 <code>Annotorious</code> 可以实现这个功能。<code>Annotorious</code> 提供了图片注释和标注功能,而且用法很简单。</p>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/06a6050f6cf64ed69e50cf65c0a3694f~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?" alt="01.gif" loading="lazy"></p>
本文分为 【快速入门】和【API讲解】两部分。
<p>【快速入门】部分包含 <code>Annotorious</code> 的安装、使用、导入导出的讲解。这几点应该是项目中比较核心的流程,给希望快速入门的工友提供一丢丢帮助。</p>
<p>【API讲解】这部分主要讲一下我认为比较常用的功能。注意:是“我认为”。</p>
<h1 data-id="heading-1">快速入门</h1>
<p>快速入门部分会讲解<code>Annotorious</code> 的安装、使用、导入和导出数据功能。</p>
<h3 data-id="heading-2">安装 Annotorious</h3>
<h3 data-id="heading-3">CDN</h3>
<pre><code lang="html"><span data-line-num="1"><span style="font-style: italic;"></span></span>
<span data-line-num="2"><span><<span>link</span> <span>rel</span>=<span>"stylesheet"</span> <span>href</span>=<span>"https://cdn.jsdelivr.net/npm/@recogito/annotorious@2.7.10/dist/annotorious.min.css"</span>></span></span>
<span data-line-num="3"></span>
<span data-line-num="4"><span style="font-style: italic;"></span></span>
<span data-line-num="5"><span><<span>script</span> <span>src</span>=<span>"https://cdn.jsdelivr.net/npm/@recogito/annotorious@2.7.10/dist/annotorious.min.js"</span>></span><span></<span>script</span>></span></span>
</code></pre>
<p>你可以把这两份文件下载到自己的项目里再引入。</p>
<h3 data-id="heading-4">NPM</h3>
<p>用以下命令安装 <code>Annotorious</code></p>
<pre><code lang="bash"><span data-line-num="1">npm install @recogito/annotorious</span>
</code></pre>
<p>然后在项目中引入</p>
<pre><code lang="js"><span data-line-num="1"><span>import</span> { <span>Annotorious</span> } <span>from</span> <span>'@recogito/annotorious'</span></span>
<span data-line-num="2"><span>import</span> <span>'@recogito/annotorious/dist/annotorious.min.css'</span></span>
</code></pre>
<h2 data-id="heading-5">使用 annotorious</h2>
<p>把 <code>Annotorious</code> 安装到项目后就可以使用了。</p>
<p><code>Annotorious</code> 的用法很简单,只需做以下2步:</p>
<ol>
<li>在html部分插入图片</li>
<li>初始化 <code>Annotorious</code>,并绑定图片元素(元素的ID或者元素本身)</li>
</ol>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f131b6e44fda4616b94f3e7aa96665d4~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?" alt="01.gif" loading="lazy"></p>
<p><code>CDN</code> 和 <code>NPM</code> 在初始化时的用法稍微有点不同。</p>
<h3 data-id="heading-6">CDN</h3>
<pre><code lang="html"><span data-line-num="1"><span><<span>img</span> <span>src</span>=<span>"./img.jpg"</span> <span>id</span>=<span>"my-image"</span> /></span></span>
<span data-line-num="2"></span>
<span data-line-num="3"><span><<span>script</span>></span><span></span></span>
<span data-line-num="4"><span>let</span> anno = <span>Annotorious</span>.<span>init</span>({</span>
<span data-line-num="5"> <span>image</span>: <span>'my-image'</span> <span style="font-style: italic;">// 元素ID</span></span>
<span data-line-num="6">})</span>
<span data-line-num="7"><span></<span>script</span>></span></span>
</code></pre>
<p>使用 <code>CDN</code> 的方式引入 <code>Annotorious</code>,在初始化时要 <code>Annotorious.init</code> 这样写。</p>
<h3 data-id="heading-7">NPM</h3>
<pre><code lang="html"><span data-line-num="1"><span><<span>img</span> <span>src</span>=<span>"./img.jpg"</span> <span>id</span>=<span>"my-image"</span> /></span></span>
<span data-line-num="2"></span>
<span data-line-num="3"><span><<span>script</span>></span><span></span></span>
<span data-line-num="4"><span>const</span> anno = <span>new</span> <span>Annotorious</span>({</span>
<span data-line-num="5"> <span>image</span>: <span>document</span>.<span>getElementById</span>(<span>'my-image'</span>) <span style="font-style: italic;">// 元素本身</span></span>
<span data-line-num="6">})</span>
<span data-line-num="7"><span></<span>script</span>></span></span>
</code></pre>
<p>使用 <code>NPM</code> 的方式引入 <code>Annotorious</code> 在初始化时需要 <code>new Annotorious</code> 这样写。</p>
<p><strong>注意:在 <code>Annotorious</code> 初始化代码最好放在你所使用的框架的页面加载完成后的生命周期函数里!</strong></p>
<h2 data-id="heading-8">导出数据 getAnnotations()</h2>
<p>如果你需要将图片上的标注保存到服务器,就需要把数据导出。</p>
<p>所用到的方法是 <code>getAnnotations()</code>。</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/982c199adc8d48d596b37645dad65b05~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?" alt="02.png" loading="lazy"></p>
<pre><code lang="html"><span data-line-num="1"><span><<span>button</span> <span>onclick</span>=<span>"save()"</span>></span>保存<span></<span>button</span>></span></span>
<span data-line-num="2"><span><<span>img</span> <span>src</span>=<span>"./44.jpg"</span> <span>id</span>=<span>"img"</span> /></span></span>
<span data-line-num="3"></span>
<span data-line-num="4"><span><<span>script</span>></span><span></span></span>
<span data-line-num="5"> <span>let</span> anno = <span>null</span></span>
<span data-line-num="6"></span>
<span data-line-num="7"> onload = <span>function</span>(<span></span>) {</span>
<span data-line-num="8"> anno = <span>Annotorious</span>.<span>init</span>({</span>
<span data-line-num="9"> <span>image</span>: <span>'img'</span></span>
<span data-line-num="10"> })</span>
<span data-line-num="11"> }</span>
<span data-line-num="12"></span>
<span data-line-num="13"> <span>function</span> <span>save</span>(<span></span>) {</span>
<span data-line-num="14"> <span>let</span> res = anno.<span>getAnnotations</span>()</span>
<span data-line-num="15"> <span>console</span>.<span>log</span>(res)</span>
<span data-line-num="16"> }</span>
<span data-line-num="17"><span></<span>script</span>></span></span>
</code></pre>
<h2 data-id="heading-9">导入数据 loadAnnotations(url)</h2>
<p><code>Annotorious</code> 可以通过 <code>loadAnnotations()</code> 方法加载数据。</p>
<p><code>loadAnnotations(url)</code> 支持传入一个 <code>URL</code> 参数,这个 <code>URL</code> 所指的是数据文件地址。</p>
<p>比如我在本地创建一个 <code>data.json</code> 文件,文件内容是使用前面讲到的 <code>getAnnotations()</code> 方法导出的数据,我的数据内容如下:</p>
<pre><code lang="json"><span data-line-num="1"><span>[</span></span>
<span data-line-num="2"> <span>{</span></span>
<span data-line-num="3"> <span>"@context"</span><span>:</span> <span>"http://www.w3.org/ns/anno.jsonld"</span><span>,</span></span>
<span data-line-num="4"> <span>"type"</span><span>:</span> <span>"Annotation"</span><span>,</span></span>
<span data-line-num="5"> <span>"body"</span><span>:</span> <span>[</span></span>
<span data-line-num="6"> <span>{</span></span>
<span data-line-num="7"> <span>"type"</span><span>:</span> <span>"TextualBody"</span><span>,</span></span>
<span data-line-num="8"> <span>"value"</span><span>:</span> <span>"1"</span><span>,</span></span>
<span data-line-num="9"> <span>"purpose"</span><span>:</span> <span>"commenting"</span></span>
<span data-line-num="10"> <span>}</span></span>
<span data-line-num="11"> <span>]</span><span>,</span></span>
<span data-line-num="12"> <span>"target"</span><span>:</span> <span>{</span></span>
<span data-line-num="13"> <span>"source"</span><span>:</span> <span>"http://127.0.0.1:5500/44.jpg"</span><span>,</span></span>
<span data-line-num="14"> <span>"selector"</span><span>:</span> <span>{</span></span>
<span data-line-num="15"> <span>"type"</span><span>:</span> <span>"FragmentSelector"</span><span>,</span></span>
<span data-line-num="16"> <span>"conformsTo"</span><span>:</span> <span>"http://www.w3.org/TR/media-frags/"</span><span>,</span></span>
<span data-line-num="17"> <span>"value"</span><span>:</span> <span>"xywh=pixel:100,100,500,300"</span></span>
<span data-line-num="18"> <span>}</span></span>
<span data-line-num="19"> <span>}</span><span>,</span></span>
<span data-line-num="20"> <span>"id"</span><span>:</span> <span>"#cabe2e71-b19f-4499-80c6-235882fd50ba"</span></span>
<span data-line-num="21"> <span>}</span></span>
<span data-line-num="22"><span>]</span></span>
</code></pre>
<p>在本地测试时,我使用了本地服务器把 <code>data.json</code> 管理起来,在浏览器可以通过 <code>http://127.0.0.1:5500/data.json</code> 访问到该文件。</p>
<p>然后再使用 <code>loadAnnotations(url)</code> 方法把数据渲染出来即可。</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c58e26d3625f4bce921b1093778bcdf4~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?" alt="04.png" loading="lazy"></p>
<pre><code lang="html"><span data-line-num="1"><span><<span>button</span> <span>onclick</span>=<span>"load()"</span>></span>加载<span></<span>button</span>></span></span>
<span data-line-num="2"><span><<span>img</span> <span>src</span>=<span>"./44.jpg"</span> <span>id</span>=<span>"img"</span> /></span></span>
<span data-line-num="3"></span>
<span data-line-num="4"><span><<span>script</span>></span><span></span></span>
<span data-line-num="5"><span>let</span> anno = <span>null</span></span>
<span data-line-num="6"></span>
<span data-line-num="7">onload = <span>function</span>(<span></span>) {</span>
<span data-line-num="8"> anno = <span>Annotorious</span>.<span>init</span>({</span>
<span data-line-num="9"> <span>image</span>: <span>'img'</span></span>
<span data-line-num="10"> })</span>
<span data-line-num="11">}</span>
<span data-line-num="12"></span>
<span data-line-num="13"><span>function</span> <span>load</span>(<span></span>) {</span>
<span data-line-num="14"> anno.<span>loadAnnotations</span>(<span>"http://127.0.0.1:5500/data.json"</span>)</span>
<span data-line-num="15">}</span>
<span data-line-num="16"><span></<span>script</span>></span></span>
</code></pre>
<p>点击加载按钮后,图片上就会出现一个选框,点击选框可以看到数据已经成功加载出来。</p>
<h2 data-id="heading-10">添加数据 addAnnotation()</h2>
<p>但在实际项目中,后台不一定会给前端返回一个文件地址,后台可能会直接返回一个json数据。</p>
<p>这时候如果使用 <code>loadAnnotations()</code> 方法加载 <code>json</code> 数据是行不通的,要通过遍历读取数据中心的 <code>data</code> 里的数据,然后调用 <code>addAnnotation()</code> 方法将元素添加到页面。</p>
<p>我使用 <code>json-server</code> 简单的在本地搭建一个服务器给前端访问对应的资源,前端用 <code>axios</code> 请求资源。</p>
<pre><code lang="html"><span data-line-num="1"><span><<span>button</span> <span>onclick</span>=<span>"load()"</span>></span>加载<span></<span>button</span>></span></span>
<span data-line-num="2"><span><<span>img</span> <span>src</span>=<span>"./44.jpg"</span> <span>id</span>=<span>"img"</span> /></span></span>
<span data-line-num="3"></span>
<span data-line-num="4"><span><<span>script</span>></span><span></span></span>
<span data-line-num="5"><span>let</span> anno = <span>null</span></span>
<span data-line-num="6"></span>
<span data-line-num="7">onload = <span>function</span>(<span></span>) {</span>
<span data-line-num="8"> anno = <span>Annotorious</span>.<span>init</span>({</span>
<span data-line-num="9"> <span>image</span>: <span>'img'</span></span>
<span data-line-num="10"> })</span>
<span data-line-num="11">}</span>
<span data-line-num="12"></span>
<span data-line-num="13"><span>function</span> <span>load</span>(<span></span>) {</span>
<span data-line-num="14"> axios.<span>get</span>(<span>'http://localhost:3000/anno'</span>)</span>
<span data-line-num="15"> .<span>then</span>(<span><span>res</span> =></span> {</span>
<span data-line-num="16"> res.<span>data</span>.<span>data</span>.<span>forEach</span>(<span><span>item</span> =></span> {</span>
<span data-line-num="17"> anno.<span>addAnnotation</span>(item)</span>
<span data-line-num="18"> })</span>
<span data-line-num="19"> })</span>
<span data-line-num="20">}</span>
<span data-line-num="21"><span></<span>script</span>></span></span>
</code></pre>
<p>很久之前写过一篇 <a href="https://juejin.cn/post/7043424909472563208" target="_blank" title="https://juejin.cn/post/7043424909472563208">《『前端必备』本地数据接口 —— json-server 从入门到膨胀》</a> 文章介绍 <code>json-server</code> 的基础用法,有兴趣的工友可以去瞧瞧。</p>
<h1 data-id="heading-11">API讲解</h1>
<p>这部分主要讲一些我关注到的功能,如果想全面了解 <code>Annotorious</code> 可以查看<a href="https://link.juejin.cn/?target=https%3A%2F%2Fannotorious.github.io%2Fapi-docs%2Fannotorious%2F" target="_blank" title="https://annotorious.github.io/api-docs/annotorious/" ref="nofollow noopener noreferrer">文档</a>。</p>
<h2 data-id="heading-12">汉化 locale</h2>
<p><code>Annotorious</code> 是根据浏览器的设置来确定使用哪种语言。</p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f8bd36ed5737475b9861f1faf874c45f~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?" alt="05.png" loading="lazy"></p>
<p>如果需要修改 <code>Annotorious</code> 使用的语言,可以在初始化时配置一下 <code>locale</code> 字段。</p>
<p>比如配置简体中文可以用 <code>zh-CN</code>,配置繁体中文可以用 <code>zh-TW</code>。</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/327def30e6684bf9b48c5321b0ac4083~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?" alt="06.png" loading="lazy"></p>
<pre><code lang="html"><span data-line-num="1"><span><<span>img</span> <span>src</span>=<span>"./img.jpg"</span> <span>id</span>=<span>"my-image"</span> /></span></span>
<span data-line-num="2"></span>
<span data-line-num="3"><span><<span>script</span>></span><span></span></span>
<span data-line-num="4"><span>let</span> anno = <span>Annotorious</span>.<span>init</span>({</span>
<span data-line-num="5"> <span>image</span>: <span>'my-image'</span>,</span>
<span data-line-num="6"> <span>locale</span>: <span>'zh-CN'</span> <span style="font-style: italic;">// 修改语言</span></span>
<span data-line-num="7">})</span>
<span data-line-num="8"><span></<span>script</span>></span></span>
</code></pre>
<h2 data-id="heading-13">自定义提示文本 messages</h2>
<p>如果想自定义按钮或者输入框的提示文本可以配置 <code>messages</code> 。</p>
<pre><code lang="html"><span data-line-num="1"><span><<span>img</span> <span>src</span>=<span>"./img.jpg"</span> <span>id</span>=<span>"my-image"</span> /></span></span>
<span data-line-num="2"></span>
<span data-line-num="3"><span><<span>script</span>></span><span></span></span>
<span data-line-num="4"></span>
<span data-line-num="5"><span style="font-style: italic;">// 创建一个包含自定义消息的对象</span></span>
<span data-line-num="6"><span>var</span> customMessages = {</span>
<span data-line-num="7"> <span>"Add a comment..."</span>: <span>"评论评论"</span>,</span>
<span data-line-num="8"> <span>"Add a reply..."</span>: <span>"回复两句"</span>,</span>
<span data-line-num="9"> <span>"Add tag..."</span>: <span>"这是标签啊"</span>,</span>
<span data-line-num="10"> <span>"Cancel"</span>: <span>"取消"</span>,</span>
<span data-line-num="11"> <span>"Close"</span>: <span>"关闭"</span>,</span>
<span data-line-num="12"> <span>"Edit"</span>: <span>"编辑~"</span>,</span>
<span data-line-num="13"> <span>"Delete"</span>: <span>"删除❌"</span>,</span>
<span data-line-num="14"> <span>"Ok"</span>: <span>"确定"</span></span>
<span data-line-num="15">}</span>
<span data-line-num="16"></span>
<span data-line-num="17"><span>let</span> anno = <span>Annotorious</span>.<span>init</span>({</span>
<span data-line-num="18"> <span>image</span>: <span>'my-image'</span>,</span>
<span data-line-num="19"> <span>messages</span>: customMessages <span style="font-style: italic;">// 自定义消息内容</span></span>
<span data-line-num="20">})</span>
<span data-line-num="21"><span></<span>script</span>></span></span>
</code></pre>
<p>如果同时配置了 <code>locale</code> 和 <code>messages</code> ,会优先使用 <code>message</code> 的值。</p>
<h2 data-id="heading-14">空注释 allowEmpty</h2>
<p>默认情况下,如果框选后没输入标签或者评论就按确定是不会保存选框的。</p>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/19c7e34362104175a7d103d276467c0f~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?" alt="07.gif" loading="lazy"></p>
<p>如果想保存空选框,可以将 <code>allowEmpty</code> 设置为 <code>true</code> 。</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/43ec930580bc4f2296900e6c235eda21~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?" alt="08.gif" loading="lazy"></p>
<pre><code lang="html"><span data-line-num="1"><span><<span>img</span> <span>src</span>=<span>"./img.jpg"</span> <span>id</span>=<span>"my-image"</span> /></span></span>
<span data-line-num="2"></span>
<span data-line-num="3"><span><<span>script</span>></span><span></span></span>
<span data-line-num="4"><span>let</span> anno = <span>Annotorious</span>.<span>init</span>({</span>
<span data-line-num="5"> <span>image</span>: <span>'my-image'</span>,</span>
<span data-line-num="6"> <span>allowEmpty</span>: <span>true</span> <span style="font-style: italic;">// 允许空注释</span></span>
<span data-line-num="7">})</span>
<span data-line-num="8"><span></<span>script</span>></span></span>
</code></pre>
<h2 data-id="heading-15">框选辅助线 crosshair</h2>
<p>有些鼠标指针可能并不是那么标准,会影响你框选的准确性。</p>
<p>如果需要非常准确去框选,可以开启辅助线功能,只需将 <code>crosshair</code> 设置为 <code>true</code> 即可。</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/eefc581019424d348eaebb9afd01d3e6~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?" alt="09.png" loading="lazy"></p>
<pre><code lang="html"><span data-line-num="1"><span><<span>img</span> <span>src</span>=<span>"./img.jpg"</span> <span>id</span>=<span>"my-image"</span> /></span></span>
<span data-line-num="2"></span>
<span data-line-num="3"><span><<span>script</span>></span><span></span></span>
<span data-line-num="4"><span>let</span> anno = <span>Annotorious</span>.<span>init</span>({</span>
<span data-line-num="5"> <span>image</span>: <span>'my-image'</span>,</span>
<span data-line-num="6"> <span>crosshair</span>: <span>true</span> <span style="font-style: italic;">// 开启辅助线</span></span>
<span data-line-num="7">})</span>
<span data-line-num="8"><span></<span>script</span>></span></span>
</code></pre>
<h2 data-id="heading-16">只读 readOnly</h2>
<p>如果不打算提供框选、添加和删除信息的操作给用户,可以将 <code>readOnly</code> 设置为 <code>true</code> 。</p>
<pre><code lang="html"><span data-line-num="1"><span><<span>img</span> <span>src</span>=<span>"./img.jpg"</span> <span>id</span>=<span>"my-image"</span> /></span></span>
<span data-line-num="2"></span>
<span data-line-num="3"><span><<span>script</span>></span><span></span></span>
<span data-line-num="4"><span>let</span> anno = <span>Annotorious</span>.<span>init</span>({</span>
<span data-line-num="5"> <span>image</span>: <span>'my-image'</span>,</span>
<span data-line-num="6"> <span>readOnly</span>: <span>true</span> <span style="font-style: italic;">// 只读模式</span></span>
<span data-line-num="7">})</span>
<span data-line-num="8"><span></<span>script</span>></span></span>
</code></pre>
<h2 data-id="heading-17">禁止编辑 disableEditor</h2>
<p>如果只需要画框框,不需要写注释,可以将 <code>disableEditor</code> 和 <code>allowEmpty</code> 同时设置为 <code>true</code>。</p>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/db475118141646998de1ceebe7c52573~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?" alt="10.gif" loading="lazy"></p>
<pre><code lang="html"><span data-line-num="1"><span><<span>img</span> <span>src</span>=<span>"./img.jpg"</span> <span>id</span>=<span>"my-image"</span> /></span></span>
<span data-line-num="2"></span>
<span data-line-num="3"><span><<span>script</span>></span><span></span></span>
<span data-line-num="4"><span>let</span> anno = <span>Annotorious</span>.<span>init</span>({</span>
<span data-line-num="5"> <span>image</span>: <span>'my-image'</span>,</span>
<span data-line-num="6"> <span>allowEmpty</span>: <span>true</span>, <span style="font-style: italic;">// 允许空注释</span></span>
<span data-line-num="7"> <span>disableEditor</span>: <span>true</span> <span style="font-style: italic;">// 禁用编辑</span></span>
<span data-line-num="8">})</span>
<span data-line-num="9"><span></<span>script</span>></span></span>
</code></pre>
<p>为什么要同时将 <code>allowEmpty</code> 设为 <code>true</code> ?</p>
<p>因为如果你不允许注释为空的话,当你点击空白处时选框就会消失。</p>
<h2 data-id="heading-18">禁止选中选框 disableSelect</h2>
<p>将 <code>disableSelect</code> 设置为 <code>true</code> 后,画布上的选框就无法再次选中了。</p>
<pre><code lang="html"><span data-line-num="1"><span><<span>img</span> <span>src</span>=<span>"./img.jpg"</span> <span>id</span>=<span>"my-image"</span> /></span></span>
<span data-line-num="2"></span>
<span data-line-num="3"><span><<span>script</span>></span><span></span></span>
<span data-line-num="4"><span>let</span> anno = <span>Annotorious</span>.<span>init</span>({</span>
<span data-line-num="5"> <span>image</span>: <span>'my-image'</span>,</span>
<span data-line-num="6"> <span>disableSelect</span>: <span>true</span> <span style="font-style: italic;">// 禁止选中选框</span></span>
<span data-line-num="7">})</span>
<span data-line-num="8"><span></<span>script</span>></span></span>
</code></pre>
<p>虽然还没想到有什么引用场景,但还是打算记录一下。</p>
<h2 data-id="heading-19">手柄半径 handleRadius</h2>
<p>箭头所指的就是手柄。</p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1928839d192445039b1634a6c95f38b5~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?" alt="11.png" loading="lazy"></p>
<p>手柄的默认半径是6。如果需要修改手柄半径可以设置 <code>handleRadius</code> 属性。</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ac53f1c8bf274e9094bd14c7884e9004~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?" alt="12.png" loading="lazy"></p>
<pre><code lang="html"><span data-line-num="1"><span><<span>img</span> <span>src</span>=<span>"./img.jpg"</span> <span>id</span>=<span>"my-image"</span> /></span></span>
<span data-line-num="2"></span>
<span data-line-num="3"><span><<span>script</span>></span><span></span></span>
<span data-line-num="4"><span>let</span> anno = <span>Annotorious</span>.<span>init</span>({</span>
<span data-line-num="5"> <span>image</span>: <span>'my-image'</span>,</span>
<span data-line-num="6"> <span>handleRadius</span>: <span>20</span> <span style="font-style: italic;">// 设置手柄半径</span></span>
<span data-line-num="7">})</span>
<span data-line-num="8"><span></<span>script</span>></span></span>
</code></pre>
<h2 data-id="heading-20">自定义选框样式</h2>
<p><code>Annotorious</code> 的选框和编辑器都是可以使用 <code>css</code> 设置样式的。</p>
<p>选框部分使用了 <code>SVG</code> ,编辑器部分直接用了 <code>HTML</code> 元素。</p>
<p>对 <code>SVG</code> 不了解的工友可以阅读 <a href="https://juejin.cn/column/7113170709945253901" target="_blank" title="https://juejin.cn/column/7113170709945253901">《SVG专栏》</a>。</p>
<p>回到 <code>Annotorious</code> ,官方也有给出一个自定义样式的案例 <a href="https://link.juejin.cn/?target=https%3A%2F%2Fannotorious.github.io%2Fguides%2Fcustomizing-styles%2F" target="_blank" title="https://annotorious.github.io/guides/customizing-styles/" ref="nofollow noopener noreferrer">《Customizing Visual Appearance》</a>。</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b6f72b1e26b749b1b98cbfbbf127f1f2~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?" alt="13.png" loading="lazy"></p>
<pre><code lang="html"><span data-line-num="1"><span><<span>style</span>></span><span></span></span>
<span data-line-num="2"> <span style="font-style: italic;">/* 选框 /</span></span>
<span data-line-num="3"> <span style="font-style: italic;">/ 隐藏外部形状-对于这种风格,我们只需要一个 /</span></span>
<span data-line-num="4"> svg<span>.a9s-annotationlayer</span> <span>.a9s-selection</span> <span>.a9s-outer</span>, </span>
<span data-line-num="5"> svg<span>.a9s-annotationlayer</span> <span>.a9s-annotation</span> <span>.a9s-outer</span> {</span>
<span data-line-num="6"> <span>display</span>:none;</span>
<span data-line-num="7"> }</span>
<span data-line-num="8"> svg<span>.a9s-annotationlayer</span> <span>.a9s-handle</span> <span>.a9s-handle-outer</span> {</span>
<span data-line-num="9"> <span>display</span>:none;</span>
<span data-line-num="10"> }</span>
<span data-line-num="11"></span>
<span data-line-num="12"> <span style="font-style: italic;">/ 虚线边框 /</span></span>
<span data-line-num="13"> svg<span>.a9s-annotationlayer</span> <span>.a9s-selection</span> <span>.a9s-inner</span>,</span>
<span data-line-num="14"> svg<span>.a9s-annotationlayer</span> <span>.a9s-annotation</span> <span>.a9s-inner</span> {</span>
<span data-line-num="15"> stroke-<span>width</span>:<span>4</span>;</span>
<span data-line-num="16"> stroke:white;</span>
<span data-line-num="17"> stroke-dasharray:<span>5</span>;</span>
<span data-line-num="18"> }</span>
<span data-line-num="19"></span>
<span data-line-num="20"> <span style="font-style: italic;">/ 选中时的填充色 /</span></span>
<span data-line-num="21"> svg<span>.a9s-annotationlayer</span> <span>.a9s-annotation</span><span>.editable</span><span>:hover</span> <span>.a9s-inner</span> {</span>
<span data-line-num="22"> fill:transparent;</span>
<span data-line-num="23"> }</span>
<span data-line-num="24"></span>
<span data-line-num="25"> <span style="font-style: italic;">/ 手柄颜色 /</span></span>
<span data-line-num="26"> svg<span>.a9s-annotationlayer</span> <span>.a9s-handle</span> <span>.a9s-handle-inner</span> {</span>
<span data-line-num="27"> fill:white;</span>
<span data-line-num="28"> stroke:white;</span>
<span data-line-num="29"> }</span>
<span data-line-num="30"></span>
<span data-line-num="31"> <span style="font-style: italic;">/ 选中选框时,遮罩层颜色 /</span></span>
<span data-line-num="32"> svg<span>.a9s-annotationlayer</span> <span>.a9s-selection-mask</span> {</span>
<span data-line-num="33"> fill:<span>rgba</span>(<span>0</span>, <span>0</span>, <span>0</span>, <span>0.6</span>);</span>
<span data-line-num="34"> }</span>
<span data-line-num="35"></span>
<span data-line-num="36"> <span style="font-style: italic;">/ 编辑器 /</span></span>
<span data-line-num="37"> <span style="font-style: italic;">/ 容器 /</span></span>
<span data-line-num="38"> <span>.r6o-editor</span> <span>.r6o-editor-inner</span> {</span>
<span data-line-num="39"> <span>box-sizing</span>: border-box;</span>
<span data-line-num="40"> <span>padding</span>: <span>10px</span>;</span>
<span data-line-num="41"> <span>border-radius</span>: <span>6px</span>;</span>
<span data-line-num="42"> <span>background</span>: <span>#F4F2DE</span>;</span>
<span data-line-num="43"> }</span>
<span data-line-num="44"></span>
<span data-line-num="45"> <span style="font-style: italic;">/ 箭头 /</span></span>
<span data-line-num="46"> <span>.r6o-editor</span> <span>.r6o-arrow</span><span>:after</span> {</span>
<span data-line-num="47"> <span>background-color</span>: <span>#F4F2DE</span>;</span>
<span data-line-num="48"> }</span>
<span data-line-num="49"></span>
<span data-line-num="50"> <span style="font-style: italic;">/ 编辑器 /</span></span>
<span data-line-num="51"> <span>.r6o-widget</span><span>.comment</span><span>.editable</span>,</span>
<span data-line-num="52"> <span>.r6o-widget</span><span>.r6o-tag</span> {</span>
<span data-line-num="53"> <span>background-color</span>: <span>#EEE3CB</span>;</span>
<span data-line-num="54"> }</span>
<span data-line-num="55"> <span>.r6o-widget</span><span>.comment</span> {</span>
<span data-line-num="56"> <span>background-color</span>: <span>#D7C0AE</span>;</span>
<span data-line-num="57"> }</span>
<span data-line-num="58"></span>
<span data-line-num="59"> <span>.r6o-editor</span> <span>.r6o-editor-inner</span> <span>.r6o-widget</span> {</span>
<span data-line-num="60"> <span>border-bottom-color</span>: <span>#7C9D96</span>;</span>
<span data-line-num="61"> }</span>
<span data-line-num="62"></span>
<span data-line-num="63"> <span>.r6o-editor</span> <span>.r6o-editor-inner</span> <span>.r6o-widget</span><span>.r6o-tag</span> {</span>
<span data-line-num="64"> <span>border-bottom</span>: none;</span>
<span data-line-num="65"> }</span>
<span data-line-num="66"></span>
<span data-line-num="67"> <span style="font-style: italic;">/ 按钮 /</span></span>
<span data-line-num="68"> <span>.r6o-editor</span> <span>.r6o-btn</span> {</span>
<span data-line-num="69"> <span>border-radius</span>: <span>100px</span>;</span>
<span data-line-num="70"> <span>background-color</span>: <span>#7C9D96</span>;</span>
<span data-line-num="71"> <span>border-color</span>: <span>#7C9D96</span>;</span>
<span data-line-num="72"> <span>color</span>: <span>#fff</span>;</span>
<span data-line-num="73"> }</span>
<span data-line-num="74"></span>
<span data-line-num="75"> <span style="font-style: italic;">/ 线框按钮 */</span></span>
<span data-line-num="76"> <span>.r6o-editor</span> <span>.r6o-btn</span><span>.outline</span> {</span>
<span data-line-num="77"> <span>color</span>: <span>#7C9D96</span>;</span>
<span data-line-num="78"> <span>background-color</span>: transparent;</span>
<span data-line-num="79"> }</span>
<span data-line-num="80"><span></<span>style</span>></span></span>
<span data-line-num="81"></span>
<span data-line-num="82"><span><<span>img</span> <span>src</span>=<span>"./img.jpg"</span> <span>id</span>=<span>"my-image"</span> /></span></span>
<span data-line-num="83"></span>
<span data-line-num="84"><span><<span>script</span>></span><span></span></span>
<span data-line-num="85"><span>let</span> anno = <span>Annotorious</span>.<span>init</span>({</span>
<span data-line-num="86"> <span>image</span>: <span>'my-image'</span>,</span>
<span data-line-num="87"> <span>locale</span>: <span>'zh-CN'</span> <span style="font-style: italic;">// 修改语言</span></span>
<span data-line-num="88">})</span>
<span data-line-num="89"><span></<span>script</span>></span></span>
</code></pre>
<p>上面这份代码选框的样式是从 <code>Annotorious</code> 官网教程搬过来的。</p>
<p>编辑器的样式我随便配了一下,工友们也可以打开浏览器控制台看 <code>Elements</code> 面板的 <code>HTML</code> 代码,根据结构去修改样式即可。</p>
<h2 data-id="heading-21">筛选功能</h2>
<p>输入时需要快速添加预选项时,可以这样配置:</p>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e054b682049c48e8b1a88a65db1e3a6c~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?" alt="14.png" loading="lazy"></p>
<pre><code lang="html"><span data-line-num="1"><span><<span>img</span> <span>src</span>=<span>"./img.jpg"</span> <span>id</span>=<span>"my-image"</span> /></span></span>
<span data-line-num="2"></span>
<span data-line-num="3"><span><<span>script</span>></span><span></span></span>
<span data-line-num="4"><span>let</span> anno = <span>Annotorious</span>.<span>init</span>({</span>
<span data-line-num="5"> <span>image</span>: <span>'my-image'</span>,</span>
<span data-line-num="6"> <span>widgets</span>: [</span>
<span data-line-num="7"> <span>'COMMENT'</span>,</span>
<span data-line-num="8"> { <span>widget</span>: <span>'TAG'</span>, <span>vocabulary</span>: [ <span>'雷猴'</span>, <span>'鲨鱼辣椒'</span>, <span>'蝎子莱莱'</span>] }</span>
<span data-line-num="9"> ]</span>
<span data-line-num="10">})</span>
<span data-line-num="11"><span></<span>script</span>></span></span>
</code></pre>
<h2 data-id="heading-22">多边形选框</h2>
<p>使用 <code>setDrawingTool(toolName)</code> 方法可以设置不同的绘制工具。</p>
<p>如果需要讲选框设置成多边形,可以传入 <code>'polygon'</code>。</p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4a428b4853ed43eebc1c56d1fe91a887~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?" alt="15.png" loading="lazy"></p>
<pre><code lang="html"><span data-line-num="1"><span><<span>img</span> <span>src</span>=<span>"./img.jpg"</span> <span>id</span>=<span>"my-image"</span> /></span></span>
<span data-line-num="2"></span>
<span data-line-num="3"><span><<span>script</span>></span><span></span></span>
<span data-line-num="4"><span>let</span> anno = <span>Annotorious</span>.<span>init</span>({</span>
<span data-line-num="5"> <span>image</span>: <span>'my-image'</span></span>
<span data-line-num="6">})</span>
<span data-line-num="7"></span>
<span data-line-num="8">anno.<span>setDrawingTool</span>(<span>"polygon"</span>)</span>
<span data-line-num="9"><span></<span>script</span>></span></span>
</code></pre>
<p>想要知道当前有哪些绘图工具,可以使用 <code>anno.listDrawingTools()</code> 方法查看</p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/83c04c98e38a49849e87648979e8400a~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?" alt="16.png" loading="lazy"></p>
<pre><code lang="html"><span data-line-num="1"><span><<span>img</span> <span>src</span>=<span>"./img.jpg"</span> <span>id</span>=<span>"my-image"</span> /></span></span>
<span data-line-num="2"></span>
<span data-line-num="3"><span><<span>script</span>></span><span></span></span>
<span data-line-num="4"><span>let</span> anno = <span>Annotorious</span>.<span>init</span>({</span>
<span data-line-num="5"> <span>image</span>: <span>'my-image'</span></span>
<span data-line-num="6">})</span>
<span data-line-num="7"></span>
<span data-line-num="8"><span>const</span> toolNames = anno.<span>listDrawingTools</span>()</span>
<span data-line-num="9"><span>console</span>.<span>log</span>(toolNames)</span>
<span data-line-num="10"><span></<span>script</span>></span></span>
</code></pre>
<h2 data-id="heading-23">其他</h2>
<p>除了上面介绍到的 <code>API</code> 外,<code>Annotorious</code> 还有很多玩法的,比如删除指定注释、清空所有注释等。</p>
<p>详情请看 <a href="https://link.juejin.cn/?target=https%3A%2F%2Fannotorious.github.io%2Fapi-docs%2Fannotorious%2F" target="_blank" title="https://annotorious.github.io/api-docs/annotorious/" ref="nofollow noopener noreferrer">《annotorious API文档》</a></p>
<h1 data-id="heading-24">插件</h1>
<p><code>Annotorious</code> 也有一些好玩的插件,有兴趣的可以看看 <a href="https://link.juejin.cn/?target=https%3A%2F%2Fannotorious.github.io%2Fplugins%2F" target="_blank" title="https://annotorious.github.io/plugins/" ref="nofollow noopener noreferrer">《Annotorious 插件推荐》</a>。</p>
<h1 data-id="heading-25">推荐阅读</h1>
<p>👍<a href="https://juejin.cn/post/7238431802299138105" target="_blank" title="https://juejin.cn/post/7238431802299138105">《提升日期处理效率:day.js 实战经验分享》</a></p>
<p>👍<a href="https://juejin.cn/post/7243357900939690042" target="_blank" title="https://juejin.cn/post/7243357900939690042">《OpenLayers.js 入门教程:打造互动地图的入门指南》</a></p>
<p>👍<a href="https://juejin.cn/post/7221550123193811002" target="_blank" title="https://juejin.cn/post/7221550123193811002">《物理世界的互动之旅:Matter.js入门指南》</a></p>
<p>👍<a href="https://juejin.cn/post/7173451612654927908" target="_blank" title="https://juejin.cn/post/7173451612654927908">《p5.js 光速入门》</a></p>
<p>👍<a href="https://juejin.cn/post/7215100383599870010" target="_blank" title="https://juejin.cn/post/7215100383599870010">《眨个眼就学会了Pixi.js》</a></p>
<p>👍<a href="https://juejin.cn/post/7026941253845516324" target="_blank" title="https://juejin.cn/post/7026941253845516324">《Fabric.js 从入门到膨胀》</a></p>
<p><strong>点赞 + 关注 + 收藏 = 学会了</strong></p>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7e789303aa77493db3ac9b9d92963520~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?" alt="640.gif" loading="lazy"></p></div><blockquote><p>本文使用 <a href="https://www.jianshu.com/p/5709df6fb58d" class="internal">文章同步助手</a> 同步</p></blockquote>
网友评论