用html5做一条线,带徒手画线条的HTML5画布

创建基于画布的绘图应用程序。

绘图应用程序没有那么多。当按钮在鼠标位置向下绘制时,听鼠标。

如果你想拥有一个响应式画布并且还包含undos等等,那么你需要从更复杂的层面开始。

绘图和显示。

首先,您应该将图形与显示区分开。这是通过创建保存绘图的屏幕外画布来完成的。它的大小是恒定的,可以由用户平移和缩放(甚至旋转)。

如果要创建线条或方框,使用离屏画布来保存绘图还可以在绘图上绘制。

有助于创建画布的一些功能

function createCanvas(width, height) {

const c = document.createElement("canvas");

c.width = width;

c.height = height;

c.ctx = c.getContext("2d");

return c;

}

const drawing = createCanvas(512,512);

您可以使用将该画布绘制到显示画布

ctx.drawImage(drawing,0,0);

该片段使用阴影和边框在画布中心绘制绘图,看起来不错。

鼠标处理程序

正确执行鼠标界面非常重要。从画布中聆听鼠标事件有一些问题。当用户拖离画布时,您不再获得任何鼠标事件。这意味着当鼠标离开画布时,您需要停止绘图,因为您不知道鼠标在返回时是否仍然处于停机状态。

要解决此问题,请聆听文档的鼠标事件。这将在按钮关闭时捕获鼠标,允许用户在您仍然获得事件时将鼠标移动到屏幕上的任何位置。如果鼠标在离开画布时上升,你仍然会得到那个事件。

注意堆栈溢出代码段窗口会阻止鼠标捕获ATM(最近的更改),因此上述鼠标行为仅限于包含该代码段的iFrame。

您也不应该从鼠标事件中进行任何渲染。某些渲染操作可能很慢,比鼠标的更新速度慢得多。如果从鼠标事件渲染,则会丢失鼠标事件。始终在鼠标事件侦听器中执行尽可能少的代码。

在代码段中,鼠标事件仅记录当前鼠标状态,如果鼠标已关闭并且绘制它将记录鼠标创建的路径。通过函数调用requestAnimationFrame同步到显示刷新率的单独循环负责呈现内容。它以大约60fps的速度运行。为了在没有任何事情发生时停止绘图,会使用一个标志来指示显示需要更新updateDisplay当有更改时,您将其设置为true updateDisplay=true;,并在下次显示硬件准备好显示时一个框架,它将绘制所有更新的内容。

画一条线

一条线只是一组连接点。在代码片段中,我创建了一个线对象。它包含构成线条的点以及线宽和颜色。

当鼠标停止时,我创建一个新的线对象并开始向其添加点。我标记显示需要更新,并在显示循环中通过其绘制方法在显示画布上绘制线条。

当鼠标向上移动时,我在绘图画布上绘制线条。这样做可以让你将一些智能应用到线上(片段很简单,对线条没有任何作用),例如使它沿着它的长度淡出。只有在鼠标启动时才会知道它的长度。

在代码片段中,当鼠标按下时我会丢弃该行,但如果您想要撤消,则会保存在数组中绘制的每一行。要撤消您,只需清除图形并重新绘制除撤消线之外的所有线条。

行对象和相关代码。

// a point object creates point from x,y coords or object that has x,y

const point = (x, y = x.y + ((x = x.x) * 0)) => ({ x, y });

// function to add a point to the line

function addPoint(x, y) { this.points.push(point(x, y)); }

// draw a line on context ctx and adds offset.x, offset.y

function drawLine(ctx, offset) {

ctx.strokeStyle = this.color;

ctx.lineWidth = this.width;

ctx.lineJoin = "round";

ctx.lineCap = "round";

ctx.beginPath();

var i = 0;

while (i < this.points.length) {

const p = this.points[i++];

ctx.lineTo(p.x + offset.x, p.y + offset.y);

}

ctx.stroke();

}

// creates a new line object

function createLine(color, width) {

return {

points: [], // the points making up the line

color, // colour of the line

width, // width of the line

add: addPoint, // function to add a point

draw: drawLine, // function to draw the whole line

};

}

示例代码段

该片段的结果比我想要的要长一点。我已经为相关代码添加了评论,但如果您有任何疑问,请在评论中提问,我会更新答案并提供更多信息。

&#13;

&#13;

// size of drawing and its starting background colour

const drawingInfo = {

width: 384 ,

height: 160,

bgColor: "white",

}

const brushSizes = [1, 2, 3, 4, 5, 6, 7, 8];

const colors = "red,orange,yellow,green,cyan,blue,purple,white,gray,black".split(",");

var currentColor = "blue";

var currentWidth = 2;

var currentSelectBrush;

var currentSelectColor;

const colorSel = document.getElementById("colorSel");

colors.forEach((color, i) => {

var swatch = document.createElement("span");

swatch.className = "swatch";

swatch.style.backgroundColor = color;

if (currentColor === color) {

swatch.className = "swatch highlight";

currentSelectColor = swatch;

} else {

swatch.className = "swatch";

}

swatch.addEventListener("click", (e) => {

currentSelectColor.className = "swatch";

currentColor = e.target.style.backgroundColor;

currentSelectColor = e.target;

currentSelectColor.className = "swatch highlight";

});

colorSel.appendChild(swatch);

})

brushSizes.forEach((brushSize, i) => {

var brush = document.createElement("canvas");

brush.width = 16;

brush.height = 16;

brush.ctx = brush.getContext("2d");

brush.ctx.beginPath();

brush.ctx.arc(8, 8, brushSize / 2, 0, Math.PI * 2);

brush.ctx.fill();

brush.brushSize = brushSize;

if (currentWidth === brushSize) {

brush.className = "swatch highlight";

currentSelectBrush = brush;

} else {

brush.className = "swatch";

}

brush.addEventListener("click", (e) => {

currentSelectBrush.className = "swatch";

currentSelectBrush = e.target;

currentSelectBrush.className = "swatch highlight";

currentWidth = e.target.brushSize;

});

colorSel.appendChild(brush);

})

const canvas = document.getElementById("can");

const mouse = createMouse().start(canvas, true);

const ctx = canvas.getContext("2d");

var updateDisplay = true; // when true the display needs updating

var ch, cw, w, h; // global canvas size vars

var currentLine;

var displayOffset = {

x: 0,

y: 0

};

// a point object creates point from x,y coords or object that has x,y

const point = (x, y = x.y + ((x = x.x) * 0)) => ({

x,

y

});

// function to add a point to the line

function addPoint(x, y) {

this.points.push(point(x, y));

}

function drawLine(ctx, offset) { // draws a line

ctx.strokeStyle = this.color;

ctx.lineWidth = this.width;

ctx.lineJoin = "round";

ctx.lineCap = "round";

ctx.beginPath();

var i = 0;

while (i < this.points.length) {

const p = this.points[i++];

ctx.lineTo(p.x + offset.x, p.y + offset.y);

}

ctx.stroke();

}

function createLine(color, width) {

return {

points: [],

color,

width,

add: addPoint,

draw: drawLine,

};

}

// creates a canvas

function createCanvas(width, height) {

const c = document.createElement("canvas");

c.width = width;

c.height = height;

c.ctx = c.getContext("2d");

return c;

}

// resize main display canvas and set global size vars

function resizeCanvas() {

ch = ((h = canvas.height = innerHeight - 32) / 2) | 0;

cw = ((w = canvas.width = innerWidth) / 2) | 0;

updateDisplay = true;

}

function createMouse() {

function preventDefault(e) { e.preventDefault() }

const mouse = {

x: 0,

y: 0,

buttonRaw: 0,

prevButton: 0

};

const bm = [1, 2, 4, 6, 5, 3]; // bit masks for mouse buttons

const mouseEvents = "mousemove,mousedown,mouseup".split(",");

const m = mouse;

// one mouse handler

function mouseMove(e) {

m.bounds = m.element.getBoundingClientRect();

m.x = e.pageX - m.bounds.left - scrollX;

m.y = e.pageY - m.bounds.top - scrollY;

if (e.type === "mousedown") {

m.buttonRaw |= bm[e.which - 1];

} else if (e.type === "mouseup") {

m.buttonRaw &= bm[e.which + 2];

}

// check if there should be a display update

if (m.buttonRaw || m.buttonRaw !== m.prevButton) {

updateDisplay = true;

}

// if the mouse is down and the prev mouse is up then start a new line

if (m.buttonRaw !== 0 && m.prevButton === 0) { // starting new line

currentLine = createLine(currentColor, currentWidth);

currentLine.add(m); // add current mouse position

} else if (m.buttonRaw !== 0 && m.prevButton !== 0) { // while mouse is down

currentLine.add(m); // add current mouse position

}

m.prevButton = m.buttonRaw; // remember the previous mouse state

e.preventDefault();

}

// starts the mouse

m.start = function(element, blockContextMenu) {

m.element = element;

mouseEvents.forEach(n => document.addEventListener(n, mouseMove));

if (blockContextMenu === true) {

document.addEventListener("contextmenu", preventDefault)

}

return m

}

return m;

}

var cursor = "crosshair";

function update(timer) { // Main update loop

cursor = "crosshair";

globalTime = timer;

// if the window size has changed resize the canvas

if (w !== innerWidth || h !== innerHeight) {

resizeCanvas()

}

if (updateDisplay) {

updateDisplay = false;

display(); // call demo code

}

ctx.canvas.style.cursor = cursor;

requestAnimationFrame(update);

}

// create a drawing canvas.

const drawing = createCanvas(drawingInfo.width, drawingInfo.height);

// fill with white

drawing.ctx.fillStyle = drawingInfo.bgColor;

drawing.ctx.fillRect(0, 0, drawing.width, drawing.height);

// function to display drawing

function display() {

ctx.clearRect(0, 0, w, h);

ctx.fillStyle = "rgba(0,0,0,0.25)";

const imgX = cw - (drawing.width / 2) | 0;

const imgY = ch - (drawing.height / 2) | 0;

// add a shadow to make it look nice

ctx.fillRect(imgX + 5, imgY + 5, drawing.width, drawing.height);

// add outline

ctx.strokeStyle = "black";

ctx.lineWidth = "2";

ctx.strokeRect(imgX, imgY, drawing.width, drawing.height);

// draw the image

ctx.drawImage(drawing, imgX, imgY);

if (mouse.buttonRaw !== 0) {

if (currentLine !== undefined) {

currentLine.draw(ctx, displayOffset); // draw line on display canvas

cursor = "none";

updateDisplay = true; // keep updating

}

} else if (mouse.buttonRaw === 0) {

if (currentLine !== undefined) {

currentLine.draw(drawing.ctx, {x: -imgX, y: -imgY }); // draw line on drawing

currentLine = undefined;

updateDisplay = true;

// next line is a quick fix to stop a slight flicker due to the current frame not showing the line

ctx.drawImage(drawing, imgX, imgY);

}

}

}

requestAnimationFrame(update);&#13;

#can {

position: absolute;

top: 32px;

left: 0px;

background-color: #AAA;

}

.colors {

border: 1px solid black;

display: inline-flex;

}

.swatch {

min-width: 16px;

min-height: 16px;

max-width: 16px;

border: 1px solid black;

display: inline-block;

margin: 2px;

cursor: pointer;

}

.highlight {

border: 1px solid red;

}&#13;

更新为了回应OP的评论,我添加了一个HTML版本,您应该能够复制和粘贴(包括在内的所有内容

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java GUI(图形用户界面)可以使用Java Swing库进行开发,绘可以使用Java Graphics2D类。下面是一个简单的Java GUI程序,该程序提供了一个绘区和一些工具,可以选择笔颜色和笔刷大小,还有橡皮擦工具。 ```java import java.awt.*; import java.awt.event.*; import javax.swing.*; public class DrawingApp extends JFrame { private JPanel canvas; private JComboBox<String> colorChooser; private JComboBox<Integer> sizeChooser; private JToggleButton eraseButton; private Color currentColor = Color.black; private int currentSize = 5; public static void main(String[] args) { DrawingApp app = new DrawingApp(); app.setVisible(true); } public DrawingApp() { setTitle("Drawing App"); setSize(800, 600); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // create canvas panel and add to frame canvas = new JPanel(); canvas.setBackground(Color.white); canvas.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { Graphics2D g = (Graphics2D) canvas.getGraphics(); g.setColor(currentColor); if (eraseButton.isSelected()) { g.setColor(canvas.getBackground()); } g.setStroke(new BasicStroke(currentSize, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); g.drawLine(e.getX(), e.getY(), e.getX(), e.getY()); } }); canvas.addMouseMotionListener(new MouseMotionAdapter() { public void mouseDragged(MouseEvent e) { Graphics2D g = (Graphics2D) canvas.getGraphics(); g.setColor(currentColor); if (eraseButton.isSelected()) { g.setColor(canvas.getBackground()); } g.setStroke(new BasicStroke(currentSize, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); g.drawLine(e.getX(), e.getY(), e.getX(), e.getY()); } }); add(canvas, BorderLayout.CENTER); // create tool panel and add to frame JPanel toolPanel = new JPanel(); add(toolPanel, BorderLayout.NORTH); colorChooser = new JComboBox<String>(new String[] {"Black", "Red", "Green", "Blue"}); colorChooser.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String color = (String) colorChooser.getSelectedItem(); switch (color) { case "Black": currentColor = Color.black; break; case "Red": currentColor = Color.red; break; case "Green": currentColor = Color.green; break; case "Blue": currentColor = Color.blue; break; } } }); toolPanel.add(new JLabel("Color:")); toolPanel.add(colorChooser); sizeChooser = new JComboBox<Integer>(new Integer[] {1, 3, 5, 10}); sizeChooser.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { currentSize = (Integer) sizeChooser.getSelectedItem(); } }); toolPanel.add(new JLabel("Size:")); toolPanel.add(sizeChooser); eraseButton = new JToggleButton("Eraser"); toolPanel.add(eraseButton); } } ``` 该程序创建了一个JFrame窗口,并在其中添加了一个绘区(JPanel)和一个工具区(JPanel)。绘区使用MouseListener和MouseMotionListener监听鼠标事件,并使用Graphics2D类绘制图形。工具区提供了选择颜色、笔刷大小和橡皮擦工具的选项。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值