手写效果
This commit is contained in:
1 parent
490bd6a482
commit
ee20d6c4bd
2 files changed
+596
-3
No files matched your search
+317
-3
@@ -22,7 +22,295 @@
|
||||
#define LCD_PIN_TE 2
|
||||
|
||||
#define ABS(X) (X >= 0 ? X : -X)
|
||||
|
||||
// 二维数组:coord[0]对应原【1】,coord[1]对应原【2】...coord[285]对应原【286】
|
||||
int coord[286][2] = {
|
||||
{69, 11}, // 0 → 【1】
|
||||
{68, 11}, // 1 → 【2】
|
||||
{68, 11}, // 2 → 【3】
|
||||
{66, 11}, // 3 → 【4】
|
||||
{64, 11}, // 4 → 【5】
|
||||
{62, 11}, // 5 → 【6】
|
||||
{59, 11}, // 6 → 【7】
|
||||
{57, 11}, // 7 → 【8】
|
||||
{55, 11}, // 8 → 【9】
|
||||
{53, 12}, // 9 → 【10】
|
||||
{51, 14}, // 10 → 【11】
|
||||
{49, 15}, // 11 → 【12】
|
||||
{48, 16}, // 12 → 【13】
|
||||
{47, 18}, // 13 → 【14】
|
||||
{46, 21}, // 14 → 【15】
|
||||
{45, 23}, // 15 → 【16】
|
||||
{45, 24}, // 16 → 【17】
|
||||
{44, 27}, // 17 → 【18】
|
||||
{44, 29}, // 18 → 【19】
|
||||
{44, 32}, // 19 → 【20】
|
||||
{44, 33}, // 20 → 【21】
|
||||
{44, 35}, // 21 → 【22】
|
||||
{46, 37}, // 22 → 【23】
|
||||
{48, 38}, // 23 → 【24】
|
||||
{51, 39}, // 24 → 【25】
|
||||
{53, 41}, // 25 → 【26】
|
||||
{57, 43}, // 26 → 【27】
|
||||
{59, 44}, // 27 → 【28】
|
||||
{62, 45}, // 28 → 【29】
|
||||
{64, 48}, // 29 → 【30】
|
||||
{67, 49}, // 30 → 【31】
|
||||
{68, 52}, // 31 → 【32】
|
||||
{70, 54}, // 32 → 【33】
|
||||
{71, 58}, // 33 → 【34】
|
||||
{72, 61}, // 34 → 【35】
|
||||
{72, 64}, // 35 → 【36】
|
||||
{72, 69}, // 36 → 【37】
|
||||
{72, 72}, // 37 → 【38】
|
||||
{71, 75}, // 38 → 【39】
|
||||
{69, 78}, // 39 → 【40】
|
||||
{68, 79}, // 40 → 【41】
|
||||
{66, 81}, // 41 → 【42】
|
||||
{65, 83}, // 42 → 【43】
|
||||
{62, 84}, // 43 → 【44】
|
||||
{60, 84}, // 44 → 【45】
|
||||
{58, 84}, // 45 → 【46】
|
||||
{55, 84}, // 46 → 【47】
|
||||
{52, 84}, // 47 → 【48】
|
||||
{50, 84}, // 48 → 【49】
|
||||
{48, 84}, // 49 → 【50】
|
||||
{47, 84}, // 50 → 【51】
|
||||
{45, 84}, // 51 → 【52】
|
||||
{42, 81}, // 52 → 【53】
|
||||
{39, 80}, // 53 → 【54】
|
||||
{117, 11}, // 54 → 【55】
|
||||
{118, 13}, // 55 → 【56】
|
||||
{119, 18}, // 56 → 【57】
|
||||
{119, 22}, // 57 → 【58】
|
||||
{119, 26}, // 58 → 【59】
|
||||
{119, 32}, // 59 → 【60】
|
||||
{119, 36}, // 60 → 【61】
|
||||
{118, 41}, // 61 → 【62】
|
||||
{118, 48}, // 62 → 【63】
|
||||
{117, 54}, // 63 → 【64】
|
||||
{116, 59}, // 64 → 【65】
|
||||
{115, 66}, // 65 → 【66】
|
||||
{115, 69}, // 66 → 【67】
|
||||
{114, 72}, // 67 → 【68】
|
||||
{113, 76}, // 68 → 【69】
|
||||
{113, 80}, // 69 → 【70】
|
||||
{113, 83}, // 70 → 【71】
|
||||
{113, 85}, // 71 → 【72】
|
||||
{113, 86}, // 72 → 【73】
|
||||
{113, 87}, // 73 → 【74】
|
||||
{113, 87}, // 74 → 【75】
|
||||
{126, 15}, // 75 → 【76】
|
||||
{126, 15}, // 76 → 【77】
|
||||
{126, 15}, // 77 → 【78】
|
||||
{126, 15}, // 78 → 【79】
|
||||
{126, 15}, // 79 → 【80】
|
||||
{131, 15}, // 80 → 【81】
|
||||
{136, 15}, // 81 → 【82】
|
||||
{139, 15}, // 82 → 【83】
|
||||
{142, 16}, // 83 → 【84】
|
||||
{144, 18}, // 84 → 【85】
|
||||
{146, 18}, // 85 → 【86】
|
||||
{148, 21}, // 86 → 【87】
|
||||
{151, 24}, // 87 → 【88】
|
||||
{152, 27}, // 88 → 【89】
|
||||
{152, 29}, // 89 → 【90】
|
||||
{153, 31}, // 90 → 【91】
|
||||
{153, 34}, // 91 → 【92】
|
||||
{153, 36}, // 92 → 【93】
|
||||
{153, 40}, // 93 → 【94】
|
||||
{151, 41}, // 94 → 【95】
|
||||
{148, 44}, // 95 → 【96】
|
||||
{147, 46}, // 96 → 【97】
|
||||
{144, 48}, // 97 → 【98】
|
||||
{141, 49}, // 98 → 【99】
|
||||
{139, 50}, // 99 → 【100】
|
||||
{136, 52}, // 100 → 【101】
|
||||
{136, 52}, // 101 → 【102】
|
||||
{135, 52}, // 102 → 【103】
|
||||
{134, 52}, // 103 → 【104】
|
||||
{133, 52}, // 104 → 【105】
|
||||
{133, 52}, // 105 → 【106】
|
||||
{133, 52}, // 106 → 【107】
|
||||
{133, 52}, // 107 → 【108】
|
||||
{135, 52}, // 108 → 【109】
|
||||
{136, 52}, // 109 → 【110】
|
||||
{139, 52}, // 110 → 【111】
|
||||
{142, 52}, // 111 → 【112】
|
||||
{145, 52}, // 112 → 【113】
|
||||
{147, 53}, // 113 → 【114】
|
||||
{148, 55}, // 114 → 【115】
|
||||
{151, 58}, // 115 → 【116】
|
||||
{152, 61}, // 116 → 【117】
|
||||
{152, 65}, // 117 → 【118】
|
||||
{152, 67}, // 118 → 【119】
|
||||
{152, 70}, // 119 → 【120】
|
||||
{153, 74}, // 120 → 【121】
|
||||
{153, 75}, // 121 → 【122】
|
||||
{153, 78}, // 122 → 【123】
|
||||
{151, 80}, // 123 → 【124】
|
||||
{148, 83}, // 124 → 【125】
|
||||
{145, 84}, // 125 → 【126】
|
||||
{142, 87}, // 126 → 【127】
|
||||
{140, 87}, // 127 → 【128】
|
||||
{137, 88}, // 128 → 【129】
|
||||
{135, 89}, // 129 → 【130】
|
||||
{132, 89}, // 130 → 【131】
|
||||
{130, 89}, // 131 → 【132】
|
||||
{127, 89}, // 132 → 【133】
|
||||
{125, 89}, // 133 → 【134】
|
||||
{123, 89}, // 134 → 【135】
|
||||
{122, 89}, // 135 → 【136】
|
||||
{122, 89}, // 136 → 【137】
|
||||
{27, 122}, // 137 → 【138】
|
||||
{27, 122}, // 138 → 【139】
|
||||
{27, 126}, // 139 → 【140】
|
||||
{27, 134}, // 140 → 【141】
|
||||
{27, 140}, // 141 → 【142】
|
||||
{26, 145}, // 142 → 【143】
|
||||
{26, 150}, // 143 → 【144】
|
||||
{25, 155}, // 144 → 【145】
|
||||
{25, 160}, // 145 → 【146】
|
||||
{25, 165}, // 146 → 【147】
|
||||
{25, 171}, // 147 → 【148】
|
||||
{24, 175}, // 148 → 【149】
|
||||
{24, 178}, // 149 → 【150】
|
||||
{23, 182}, // 150 → 【151】
|
||||
{23, 184}, // 151 → 【152】
|
||||
{23, 185}, // 152 → 【153】
|
||||
{23, 185}, // 153 → 【154】
|
||||
{23, 185}, // 154 → 【155】
|
||||
{23, 185}, // 155 → 【156】
|
||||
{62, 133}, // 156 → 【157】
|
||||
{62, 133}, // 157 → 【158】
|
||||
{62, 133}, // 158 → 【159】
|
||||
{62, 133}, // 159 → 【160】
|
||||
{56, 139}, // 160 → 【161】
|
||||
{49, 143}, // 161 → 【162】
|
||||
{43, 147}, // 162 → 【163】
|
||||
{39, 150}, // 163 → 【164】
|
||||
{36, 152}, // 164 → 【165】
|
||||
{35, 152}, // 165 → 【166】
|
||||
{33, 154}, // 166 → 【167】
|
||||
{32, 155}, // 167 → 【168】
|
||||
{32, 155}, // 168 → 【169】
|
||||
{32, 155}, // 169 → 【170】
|
||||
{32, 155}, // 170 → 【171】
|
||||
{32, 155}, // 171 → 【172】
|
||||
{32, 155}, // 172 → 【173】
|
||||
{34, 155}, // 173 → 【174】
|
||||
{39, 160}, // 174 → 【175】
|
||||
{45, 164}, // 175 → 【176】
|
||||
{52, 170}, // 176 → 【177】
|
||||
{58, 174}, // 177 → 【178】
|
||||
{61, 176}, // 178 → 【179】
|
||||
{65, 178}, // 179 → 【180】
|
||||
{67, 179}, // 180 → 【181】
|
||||
{68, 180}, // 181 → 【182】
|
||||
{68, 180}, // 182 → 【183】
|
||||
{69, 181}, // 183 → 【184】
|
||||
{70, 181}, // 184 → 【185】
|
||||
{70, 181}, // 185 → 【186】
|
||||
{72, 182}, // 186 → 【187】
|
||||
{87, 135}, // 187 → 【188】
|
||||
{87, 135}, // 188 → 【189】
|
||||
{87, 135}, // 189 → 【190】
|
||||
{90, 133}, // 190 → 【191】
|
||||
{95, 132}, // 191 → 【192】
|
||||
{99, 132}, // 192 → 【193】
|
||||
{103, 131}, // 193 → 【194】
|
||||
{107, 131}, // 194 → 【195】
|
||||
{109, 131}, // 195 → 【196】
|
||||
{111, 131}, // 196 → 【197】
|
||||
{114, 131}, // 197 → 【198】
|
||||
{116, 131}, // 198 → 【199】
|
||||
{118, 131}, // 199 → 【200】
|
||||
{119, 131}, // 200 → 【201】
|
||||
{120, 131}, // 201 → 【202】
|
||||
{120, 131}, // 202 → 【203】
|
||||
{120, 131}, // 203 → 【204】
|
||||
{120, 131}, // 204 → 【205】
|
||||
{120, 131}, // 205 → 【206】
|
||||
{118, 137}, // 206 → 【207】
|
||||
{116, 141}, // 207 → 【208】
|
||||
{112, 147}, // 208 → 【209】
|
||||
{108, 152}, // 209 → 【210】
|
||||
{105, 155}, // 210 → 【211】
|
||||
{101, 159}, // 211 → 【212】
|
||||
{99, 162}, // 212 → 【213】
|
||||
{94, 167}, // 213 → 【214】
|
||||
{91, 171}, // 214 → 【215】
|
||||
{87, 175}, // 215 → 【216】
|
||||
{85, 178}, // 216 → 【217】
|
||||
{84, 178}, // 217 → 【218】
|
||||
{83, 180}, // 218 → 【219】
|
||||
{83, 180}, // 219 → 【220】
|
||||
{83, 180}, // 220 → 【221】
|
||||
{83, 180}, // 221 → 【222】
|
||||
{85, 180}, // 222 → 【223】
|
||||
{88, 181}, // 223 → 【224】
|
||||
{96, 181}, // 224 → 【225】
|
||||
{102, 181}, // 225 → 【226】
|
||||
{106, 181}, // 226 → 【227】
|
||||
{111, 181}, // 227 → 【228】
|
||||
{114, 181}, // 228 → 【229】
|
||||
{116, 181}, // 229 → 【230】
|
||||
{117, 181}, // 230 → 【231】
|
||||
{119, 181}, // 231 → 【232】
|
||||
{120, 181}, // 232 → 【233】
|
||||
{122, 181}, // 233 → 【234】
|
||||
{124, 181}, // 234 → 【235】
|
||||
{156, 124}, // 235 → 【236】
|
||||
{156, 124}, // 236 → 【237】
|
||||
{156, 124}, // 237 → 【238】
|
||||
{155, 129}, // 238 → 【239】
|
||||
{153, 138}, // 239 → 【240】
|
||||
{151, 143}, // 240 → 【241】
|
||||
{150, 148}, // 241 → 【242】
|
||||
{148, 152}, // 242 → 【243】
|
||||
{148, 155}, // 243 → 【244】
|
||||
{147, 158}, // 244 → 【245】
|
||||
{146, 162}, // 245 → 【246】
|
||||
{145, 165}, // 246 → 【247】
|
||||
{145, 169}, // 247 → 【248】
|
||||
{145, 171}, // 248 → 【249】
|
||||
{145, 173}, // 249 → 【250】
|
||||
{145, 174}, // 250 → 【251】
|
||||
{145, 174}, // 251 → 【252】
|
||||
{145, 174}, // 252 → 【253】
|
||||
{145, 174}, // 253 → 【254】
|
||||
{165, 129}, // 254 → 【255】
|
||||
{166, 134}, // 255 → 【256】
|
||||
{168, 140}, // 256 → 【257】
|
||||
{169, 144}, // 257 → 【258】
|
||||
{171, 150}, // 258 → 【259】
|
||||
{171, 153}, // 259 → 【260】
|
||||
{173, 156}, // 260 → 【261】
|
||||
{173, 160}, // 261 → 【262】
|
||||
{176, 166}, // 262 → 【263】
|
||||
{176, 169}, // 263 → 【264】
|
||||
{177, 171}, // 264 → 【265】
|
||||
{178, 174}, // 265 → 【266】
|
||||
{179, 177}, // 266 → 【267】
|
||||
{180, 179}, // 267 → 【268】
|
||||
{180, 179}, // 268 → 【269】
|
||||
{180, 179}, // 269 → 【270】
|
||||
{180, 179}, // 270 → 【271】
|
||||
{180, 179}, // 271 → 【272】
|
||||
{180, 179}, // 272 → 【273】
|
||||
{182, 176}, // 273 → 【274】
|
||||
{184, 171}, // 274 → 【275】
|
||||
{188, 161}, // 275 → 【276】
|
||||
{191, 152}, // 276 → 【277】
|
||||
{193, 145}, // 277 → 【278】
|
||||
{194, 138}, // 278 → 【279】
|
||||
{196, 132}, // 279 → 【280】
|
||||
{197, 127}, // 280 → 【281】
|
||||
{199, 124}, // 281 → 【282】
|
||||
{199, 121}, // 282 → 【283】
|
||||
{200, 118}, // 283 → 【284】
|
||||
{200, 118}, // 284 → 【285】
|
||||
{201, 118} // 285 → 【286】
|
||||
};
|
||||
// 绘制测试图案
|
||||
// 画两条边缘线和一个中心点
|
||||
bool is_rotated = 0; // 跟踪旋转状态
|
||||
@@ -259,7 +547,33 @@ void lcd_circle_anim_task(void* arg)
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
}
|
||||
}
|
||||
// Replay recorded coordinates as a brush stroke: use coord[] points (20ms interval), radius 10px
|
||||
void lcd_coord_anim_task(void* arg)
|
||||
{
|
||||
const uint8_t brush_r = 10;
|
||||
const int total = sizeof(coord) / sizeof(coord[0]);
|
||||
int idx = 0;
|
||||
|
||||
// Clear screen at start
|
||||
if (buffer_mutex) xSemaphoreTakeRecursive(buffer_mutex, portMAX_DELAY);
|
||||
for (int yy = 0; yy < 200; yy++)
|
||||
for (int xx = 0; xx < 200; xx++)
|
||||
lcd_draw_bit(xx, yy, 0);
|
||||
if (buffer_mutex) xSemaphoreGiveRecursive(buffer_mutex);
|
||||
|
||||
while (1) {
|
||||
int x = coord[idx][0];
|
||||
int y = coord[idx][1];
|
||||
|
||||
// Draw brush at recorded position (trail persists)
|
||||
lcd_paint_brush((uint8_t)x, (uint8_t)y, brush_r, 1);
|
||||
|
||||
idx++;
|
||||
if (idx >= total) idx = 0; // loop replay
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(20)); // recorded at 20ms intervals
|
||||
}
|
||||
}
|
||||
void app_main(void)
|
||||
{
|
||||
/*创建SPI总线*/
|
||||
@@ -315,8 +629,8 @@ void app_main(void)
|
||||
// Start background LCD refresh task (50Hz)
|
||||
xTaskCreate(lcd_task, "lcd_task", 4096, panel_handle, 5, NULL);
|
||||
|
||||
// Start circle animation task (uses lcd_paint_brush)
|
||||
xTaskCreate(lcd_circle_anim_task, "lcd_circle", 4096, NULL, 5, NULL);
|
||||
// Start coordinate replay animation task (uses lcd_paint_brush, 20ms per point)
|
||||
xTaskCreate(lcd_coord_anim_task, "lcd_coord", 4096, NULL, 5, NULL);
|
||||
|
||||
// Immediate initial refresh
|
||||
lcd_display(panel_handle);
|
||||
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>像素风画布 - 纯原生秒开版</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: "Microsoft YaHei", sans-serif;
|
||||
}
|
||||
body {
|
||||
background: #f5f7fa;
|
||||
padding: 15px;
|
||||
}
|
||||
/* 主卡片容器,模拟Element样式,层级清晰 */
|
||||
.main-card {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
||||
border: 1px solid #ebeef5;
|
||||
}
|
||||
/* 像素风画布核心:无抗锯齿+像素化,严格200x200 */
|
||||
#drawCanvas {
|
||||
border: 1px solid #2c3e50;
|
||||
background: #f8f8f8;
|
||||
cursor: crosshair;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
/* 标题样式 */
|
||||
.canvas-title {
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
/* 提示文字 */
|
||||
.canvas-tip {
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
color: #67c23a;
|
||||
margin: 0 0 18px 0;
|
||||
}
|
||||
/* 画布居中容器 */
|
||||
.canvas-box {
|
||||
text-align: center;
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
/* 控制组:自动换行,绝不挤压,滑块+按钮100%显示 */
|
||||
.control-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px 15px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
/* 笔刷半径滑块:最小宽度,不缩成线 */
|
||||
#radiusSlider {
|
||||
min-width: 180px;
|
||||
max-width: 250px;
|
||||
flex: 1;
|
||||
height: 6px;
|
||||
}
|
||||
/* 半径数值显示 */
|
||||
#radiusValue {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
width: 45px;
|
||||
text-align: center;
|
||||
}
|
||||
/* 清除按钮:红色显眼,固定最小宽度,文字不折行 */
|
||||
#clearBtn {
|
||||
min-width: 140px;
|
||||
white-space: nowrap;
|
||||
padding: 6px 16px;
|
||||
background: #f56c6c;
|
||||
color: #fff;
|
||||
border: 1px solid #f56c6c;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
#clearBtn:hover {
|
||||
background: #f78989;
|
||||
border-color: #f78989;
|
||||
}
|
||||
/* 分割线 */
|
||||
.divider {
|
||||
height: 1px;
|
||||
background: #ebeef5;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
/* 坐标记录区标题 */
|
||||
.record-title {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
/* 坐标记录容器:固定高度,滚动显示 */
|
||||
#recordArea {
|
||||
height: 220px;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
overflow-y: auto;
|
||||
font-size: 14px;
|
||||
line-height: 2;
|
||||
color: #333;
|
||||
}
|
||||
/* 空记录提示 */
|
||||
.empty-tip {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
}
|
||||
/* 坐标记录项 */
|
||||
.record-item {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main-card">
|
||||
<h3 class="canvas-title">像素风画布 (200×200px)</h3>
|
||||
<p class="canvas-tip">按住左键绘制 | 圆形像素笔刷 | 20ms自动记录坐标</p>
|
||||
|
||||
<!-- 200x200像素画布 -->
|
||||
<div class="canvas-box">
|
||||
<canvas id="drawCanvas" width="200" height="200"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- 控制区:笔刷滑块+数值+清除按钮(100%显示) -->
|
||||
<div class="control-group">
|
||||
<span style="font-size: 14px;">笔刷半径:</span>
|
||||
<input type="range" id="radiusSlider" min="1" max="20" step="1" value="5">
|
||||
<span id="radiusValue">5</span>px
|
||||
<button id="clearBtn">清除图像&记录</button>
|
||||
</div>
|
||||
|
||||
<!-- 坐标记录区 -->
|
||||
<div class="divider"></div>
|
||||
<h4 class="record-title">坐标记录(画布相对坐标)</h4>
|
||||
<div id="recordArea">
|
||||
<span class="empty-tip">按住鼠标在画布绘制,自动记录笔刷中心坐标~</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ========== 获取DOM元素(原生直接获取,无依赖) ==========
|
||||
const canvas = document.getElementById('drawCanvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const radiusSlider = document.getElementById('radiusSlider');
|
||||
const radiusValue = document.getElementById('radiusValue');
|
||||
const clearBtn = document.getElementById('clearBtn');
|
||||
const recordArea = document.getElementById('recordArea');
|
||||
|
||||
// ========== 全局状态(核心功能变量) ==========
|
||||
let brushRadius = 5; // 笔刷半径
|
||||
let isDrawing = false; // 是否正在绘制
|
||||
let lastX = 0; // 笔刷中心最后X坐标
|
||||
let lastY = 0; // 笔刷中心最后Y坐标
|
||||
let recordTimer = null; // 20ms坐标记录定时器
|
||||
let recordCount = 0; // 坐标记录序号
|
||||
|
||||
// ========== 初始化画布(关闭抗锯齿,保证圆形像素风) ==========
|
||||
function initCanvas() {
|
||||
// 全浏览器关闭抗锯齿,圆形笔刷无模糊,纯像素质感
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
ctx.msImageSmoothingEnabled = false;
|
||||
ctx.webkitImageSmoothingEnabled = false;
|
||||
ctx.mozImageSmoothingEnabled = false;
|
||||
}
|
||||
|
||||
// ========== 绑定所有事件(原生事件,无兼容问题) ==========
|
||||
function bindEvents() {
|
||||
// 画布鼠标事件:绘制核心
|
||||
canvas.onmousedown = startDraw;
|
||||
canvas.onmousemove = draw;
|
||||
canvas.onmouseup = stopDraw;
|
||||
canvas.onmouseout = stopDraw;
|
||||
// 滑块事件:实时调节笔刷半径
|
||||
radiusSlider.oninput = updateRadius;
|
||||
// 按钮事件:一键清除
|
||||
clearBtn.onclick = clearAll;
|
||||
}
|
||||
|
||||
// ========== 笔刷半径实时更新(滑块拖动/输入同步) ==========
|
||||
function updateRadius() {
|
||||
brushRadius = parseInt(this.value);
|
||||
radiusValue.textContent = brushRadius;
|
||||
}
|
||||
|
||||
// ========== 开始绘制:启动20ms坐标记录 ==========
|
||||
function startDraw(e) {
|
||||
isDrawing = true;
|
||||
// 获取画布内精准相对坐标(取整,贴合像素)
|
||||
[lastX, lastY] = getCanvasXY(e);
|
||||
// 启动定时器,每20ms记录一次笔刷中心坐标
|
||||
recordTimer = setInterval(() => {
|
||||
recordCoord(lastX, lastY);
|
||||
}, 20);
|
||||
}
|
||||
|
||||
// ========== 核心绘制:圆形像素笔刷(无抗锯齿) ==========
|
||||
function draw(e) {
|
||||
if (!isDrawing) return;
|
||||
// 更新当前笔刷中心坐标
|
||||
[lastX, lastY] = getCanvasXY(e);
|
||||
// 绘制圆形笔刷
|
||||
ctx.beginPath();
|
||||
ctx.arc(lastX, lastY, brushRadius, 0, Math.PI * 2); // 0-360度完整圆形
|
||||
ctx.fillStyle = '#2c3e50'; // 笔刷颜色(深灰,可自定义)
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
// ========== 停止绘制:清除定时器,结束记录 ==========
|
||||
function stopDraw() {
|
||||
if (!isDrawing) return;
|
||||
isDrawing = false;
|
||||
clearInterval(recordTimer);
|
||||
recordTimer = null;
|
||||
}
|
||||
|
||||
// ========== 工具方法:获取画布内精准相对坐标 ==========
|
||||
function getCanvasXY(e) {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
// 减去画布偏移,取整得到像素级精准坐标
|
||||
return [
|
||||
Math.round(e.clientX - rect.left),
|
||||
Math.round(e.clientY - rect.top)
|
||||
];
|
||||
}
|
||||
|
||||
// ========== 坐标记录:20ms一次,追加到记录区 ==========
|
||||
function recordCoord(x, y) {
|
||||
recordCount++;
|
||||
// 移除空提示
|
||||
if (recordArea.querySelector('.empty-tip')) {
|
||||
recordArea.innerHTML = '';
|
||||
}
|
||||
// 新记录放顶部,方便查看最新坐标
|
||||
const recordItem = `<span class="record-item">【${recordCount}】X: ${x}, Y: ${y}</span>`;
|
||||
recordArea.innerHTML = recordItem + recordArea.innerHTML;
|
||||
}
|
||||
|
||||
// ========== 一键清除:画布+记录+所有状态全重置 ==========
|
||||
function clearAll() {
|
||||
// 1. 清空整个画布
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
// 2. 清空坐标记录,恢复空提示
|
||||
recordArea.innerHTML = '<span class="empty-tip">按住鼠标在画布绘制,自动记录笔刷中心坐标~</span>';
|
||||
// 3. 重置所有状态
|
||||
isDrawing = false;
|
||||
lastX = 0;
|
||||
lastY = 0;
|
||||
recordCount = 0;
|
||||
// 4. 清除定时器(防止绘制中清除仍在记录)
|
||||
if (recordTimer) {
|
||||
clearInterval(recordTimer);
|
||||
recordTimer = null;
|
||||
}
|
||||
// 可选:清除成功提示(原生alert,无依赖)
|
||||
alert('清除成功!画布和记录已重置');
|
||||
}
|
||||
|
||||
// ========== 页面初始化:执行核心方法 ==========
|
||||
window.onload = function() {
|
||||
initCanvas(); // 初始化像素画布
|
||||
bindEvents(); // 绑定所有事件
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user