1 前言
contenteditable可以使html元素实现可编辑功能,基于此属性,以及上一篇文章的基础,我们可以实现一个简单的画布功能呢。
有了画布自然可以做很多事情,比如绘画、做一些基础的图片都是非常不错的功能。
对比一些现有的第三方库来说,给有优缺点,因此通过contenteditable自定义适合自己的画布是很有必要的。
2 样式设计
样式定义基础画布大小、颜色等工能。
<style>
#canvas {
width: 500px;
height: 500px;
border: 1px solid #000;
background-color: #f0f0f0;
user-select: none;
position: relative;
}
#canvas[contenteditable="true"] {
outline: none;
cursor: crosshair;
}
.brush {
position: absolute;
width: 5px;
height: 5px;
background-color: black;
pointer-events: none;
border-radius: 50%;
}
</style>
html
<div style="display: flex;justify-content: center;align-items: center;flex-direction: column">
<div id="canvas" contenteditable="true"></div>
<ul id="log"></ul>
</div>
3 功能设计
<script>
const canvas = document.getElementById("canvas");
let isDrawing = false;
canvas.addEventListener("mousedown", (e) => {
isDrawing = true;
draw(e);
})
canvas.addEventListener('mouseup', () => {
isDrawing = false;
});
canvas.addEventListener('mousemove', (e) => {
if (isDrawing) {
draw(e);
}
});
function draw(e) {
console.log(e)
logEvent('Keydown 事件', `${e}`);
const brush = document.createElement('div');
brush.classList.add('brush');
brush.style.left = `${e.offsetX - 2}px`;
brush.style.top = `${e.offsetY - 2}px`;
canvas.appendChild(brush)
}
</script>
4 日志功能
用于实时输出当前正在调试的时间,便于查看,就不用F12了。
<script>
const logList = document.getElementById('log');
function logEvent(eventType, additionalInfo = '') {
const listItem = document.createElement('li');
const currentTime = new Date().toLocaleTimeString();
listItem.textContent = `[${currentTime}] ${eventType} ${additionalInfo}`.trim();
logList.appendChild(listItem);
trimLog();
}
function trimLog() {
if (logList.children.length > 5) {
logList.removeChild(logList.firstChild);
}
}
setInterval(trimLog, 500);
</script>
5 最终效果

<h2><a id="1__0"></a>1 前言</h2>
<p>contenteditable可以使html元素实现可编辑功能,基于此属性,以及上一篇文章的基础,我们可以实现一个简单的画布功能呢。</p>
<p>有了画布自然可以做很多事情,比如绘画、做一些基础的图片都是非常不错的功能。</p>
<p>对比一些现有的第三方库来说,给有优缺点,因此通过contenteditable自定义适合自己的画布是很有必要的。</p>
<h2><a id="2__8"></a>2 样式设计</h2>
<p>样式定义基础画布大小、颜色等工能。</p>
<pre><div class="hljs"><code class="lang-css"><style>
<span class="hljs-comment">/* 画布样式 */</span>
<span class="hljs-selector-id">#canvas</span> {
<span class="hljs-attribute">width</span>: <span class="hljs-number">500px</span>;
<span class="hljs-attribute">height</span>: <span class="hljs-number">500px</span>;
<span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#000</span>;
<span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f0f0f0</span>;
user-select: none; <span class="hljs-comment">/* 禁止选择文本 */</span>
<span class="hljs-attribute">position</span>: relative; <span class="hljs-comment">/* 画布的定位方式,用于定位画笔 */</span>
}
<span class="hljs-comment">/* 使contenteditable元素在点击时没有默认的轮廓样式,并设置光标为十字形 */</span>
<span class="hljs-selector-id">#canvas</span><span class="hljs-selector-attr">[contenteditable=<span class="hljs-string">"true"</span>]</span> {
<span class="hljs-attribute">outline</span>: none; <span class="hljs-comment">/*去除点击时的默认轮廓*/</span>
<span class="hljs-attribute">cursor</span>: crosshair; <span class="hljs-comment">/*设置光标为十字形*/</span>
}
<span class="hljs-comment">/* 画笔样式 */</span>
<span class="hljs-selector-class">.brush</span> {
<span class="hljs-attribute">position</span>: absolute; <span class="hljs-comment">/* 使用绝对定位, 使得画笔可以随鼠标移动 */</span>
<span class="hljs-attribute">width</span>: <span class="hljs-number">5px</span>;
<span class="hljs-attribute">height</span>: <span class="hljs-number">5px</span>;
<span class="hljs-attribute">background-color</span>: black;
<span class="hljs-attribute">pointer-events</span>: none; <span class="hljs-comment">/*不响应鼠标事件,让鼠标事件只作用于画布*/</span>
<span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
}
</style>
</code></div></pre>
<p>html</p>
<pre><div class="hljs"><code class="lang-html"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"display: flex;justify-content: center;align-items: center;flex-direction: column"</span>></span>
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"canvas"</span> <span class="hljs-attr">contenteditable</span>=<span class="hljs-string">"true"</span>></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
<span class="hljs-tag"><<span class="hljs-name">ul</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"log"</span>></span><span class="hljs-tag"></<span class="hljs-name">ul</span>></span>
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
</code></div></pre>
<h2><a id="3__53"></a>3 功能设计</h2>
<pre><div class="hljs"><code class="lang-javascript"><script>
<span class="hljs-keyword">const</span> canvas = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">"canvas"</span>);
<span class="hljs-comment">// 初始化会话状态,默认未绘画</span>
<span class="hljs-keyword">let</span> isDrawing = <span class="hljs-literal">false</span>;
<span class="hljs-comment">// 鼠标按下事件:开始绘画</span>
canvas.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">"mousedown"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =></span> {
isDrawing = <span class="hljs-literal">true</span>;
<span class="hljs-title function_">draw</span>(e);
})
<span class="hljs-comment">// 鼠标松开事件:停止绘画</span>
canvas.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'mouseup'</span>, <span class="hljs-function">() =></span> {
isDrawing = <span class="hljs-literal">false</span>; <span class="hljs-comment">// 设置绘画状态为false,表示停止绘画</span>
});
<span class="hljs-comment">// 鼠标移动事件:如果正在绘画,则继续绘制</span>
canvas.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'mousemove'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =></span> {
<span class="hljs-keyword">if</span> (isDrawing) { <span class="hljs-comment">// 如果处于绘画状态,则调用绘制函数</span>
<span class="hljs-title function_">draw</span>(e);
}
});
<span class="hljs-comment">/**
* 绘制函数:用于在画布上创建一个小圆点作为画笔
* */</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">draw</span>(<span class="hljs-params">e</span>) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(e)
<span class="hljs-title function_">logEvent</span>(<span class="hljs-string">'Keydown 事件'</span>, <span class="hljs-string">`<span class="hljs-subst">${e}</span>`</span>);
<span class="hljs-comment">// 创建一个新的div元素,用于表示画笔</span>
<span class="hljs-keyword">const</span> brush = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">createElement</span>(<span class="hljs-string">'div'</span>);
brush.<span class="hljs-property">classList</span>.<span class="hljs-title function_">add</span>(<span class="hljs-string">'brush'</span>);
<span class="hljs-comment">// 设置画笔的位置,基于鼠标的当前位置,并且让画笔的中心与鼠标位置对齐</span>
brush.<span class="hljs-property">style</span>.<span class="hljs-property">left</span> = <span class="hljs-string">`<span class="hljs-subst">${e.offsetX - <span class="hljs-number">2</span>}</span>px`</span>; <span class="hljs-comment">// 横坐标位置,减去2px使画笔居中</span>
brush.<span class="hljs-property">style</span>.<span class="hljs-property">top</span> = <span class="hljs-string">`<span class="hljs-subst">${e.offsetY - <span class="hljs-number">2</span>}</span>px`</span>; <span class="hljs-comment">// 纵坐标位置,减去2px使画笔居中</span>
<span class="hljs-comment">// 将创建的画笔元素添加到画布中</span>
canvas.<span class="hljs-title function_">appendChild</span>(brush)
}
</script>
</code></div></pre>
<h2><a id="4__101"></a>4 日志功能</h2>
<p>用于实时输出当前正在调试的时间,便于查看,就不用F12了。</p>
<pre><div class="hljs"><code class="lang-javascript"><script>
<span class="hljs-keyword">const</span> logList = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'log'</span>);
<span class="hljs-comment">// 工具函数:记录事件日志</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">logEvent</span>(<span class="hljs-params">eventType, additionalInfo = <span class="hljs-string">''</span></span>) {
<span class="hljs-keyword">const</span> listItem = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">createElement</span>(<span class="hljs-string">'li'</span>);
<span class="hljs-keyword">const</span> currentTime = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>().<span class="hljs-title function_">toLocaleTimeString</span>(); <span class="hljs-comment">// 获取当前时间</span>
listItem.<span class="hljs-property">textContent</span> = <span class="hljs-string">`[<span class="hljs-subst">${currentTime}</span>] <span class="hljs-subst">${eventType}</span> <span class="hljs-subst">${additionalInfo}</span>`</span>.<span class="hljs-title function_">trim</span>();
<span class="hljs-comment">// listItem.textContent = `${eventType} ${additionalInfo}`.trim();</span>
logList.<span class="hljs-title function_">appendChild</span>(listItem);
<span class="hljs-title function_">trimLog</span>();
}
<span class="hljs-comment">// 限制日志长度,防止过多</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">trimLog</span>(<span class="hljs-params"></span>) {
<span class="hljs-keyword">if</span> (logList.<span class="hljs-property">children</span>.<span class="hljs-property">length</span> > <span class="hljs-number">5</span>) {
logList.<span class="hljs-title function_">removeChild</span>(logList.<span class="hljs-property">firstChild</span>);
}
}
<span class="hljs-built_in">setInterval</span>(trimLog, <span class="hljs-number">500</span>);
</script>
</code></div></pre>
<h2><a id="5__130"></a>5 最终效果</h2>
<p><img src="https://static.couragesteak.com/article/16a7b27502ff8203f2fd6e8c1c571971.png" alt="image.png" /></p>
留言