如何修改Canvas中context.drawImage图片源实现十字标记变色
实现Canvas动态切换十字标记颜色(新标记红,旧标记蓝)
我来帮你解决这个问题!你现在的思路方向是对的,但现有代码里有几个小问题导致效果可能不符合预期——比如每次draw()执行都会创建新的定时器,还有内部定时器引用的i会因为闭包问题指向最新值,导致画错位置。而且Canvas是即时渲染模式,直接在旧标记上覆盖不如维护状态后整体重绘来得可靠。
核心思路调整
- 预加载图片:提前加载红色和蓝色十字图片,避免每次绘制重复请求资源
- 维护标记状态数组:用数组保存每个标记的坐标和「是否为新标记」的状态,新标记显示红色,2秒后切换为蓝色
- 单一主定时器:只创建一个定时器负责添加新标记,避免定时器叠加
- 用requestAnimationFrame重绘:持续刷新画布,每次重绘时根据标记状态绘制对应颜色的十字,确保画面清晰一致
修正后的完整代码
<div style="width : 75%;margin : auto;"> <canvas id="Canvas" width="954" height="267"></canvas> <button class="play">Play</button> <button class="pause">Pause</button> </div> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script> var canvas = document.getElementById('Canvas'); var context = canvas.getContext("2d"); // 预加载所有图片,确保加载完成再使用 var mapSprite = new Image(); var crossRed = new Image(); var crossBlue = new Image(); var imagesLoaded = 0; var totalImages = 3; function checkImagesLoaded() { imagesLoaded++; if (imagesLoaded === totalImages) { init(); // 所有图片加载完成后初始化 } } mapSprite.src = "v1.png"; mapSprite.onload = checkImagesLoaded; crossRed.src = "cross.png"; crossRed.onload = checkImagesLoaded; crossBlue.src = "cross-blue.png"; crossBlue.onload = checkImagesLoaded; var array_x = [351,315,353,352,318,485,351,748,568,645,373,350,572,374,650,240,240,320,707,235,402,539,256,410,433,405,353,745,404,320,514,322,692,474,293,418,418,690,237,513,378,478,433,405,285,429,458,622,568,536,564,389,641,534,567,454,575,482,459,539,431,327,464,374,348,520,279,378,509,516,232,744,351,710,481,540,429,642,537,459,513,689,324,463,375,747,431,265,484,591,458,460,485,407,720,483,376,520,691,376,232,413,536,430,653,714,484,715,629,573,263,408,436,300,544,407,432,568,536,462,485,346,610,524]; var array_y = [18,18,181,228,60,232,34,32,26,37,65,62,228,180,91,83,83,81,88,62,35,65,52,155,23,90,94,95,230,232,15,105,37,68,47,186,186,93,14,65,22,10,230,68,18,91,226,22,150,227,89,43,76,92,62,12,187,96,78,33,35,180,151,230,151,154,95,96,31,233,39,77,79,20,30,165,65,51,18,62,96,19,150,33,79,19,35,23,210,20,180,95,184,19,76,64,36,186,73,151,26,192,153,156,24,36,165,97,85,78,77,101,184,22,177,212,210,166,79,210,150,168,45,18]; var isPaused = false; var markers = []; // 保存所有标记的状态:{x, y, isNew} var currentIndex = 0; var addMarkerTimer = null; var animationId = null; function init() { // 初始化画布样式 context.font = "15px Georgia"; context.textAlign = "center"; // 启动添加标记的定时器(每隔5秒添加一个) addMarkerTimer = setInterval(function() { if (!isPaused && currentIndex < array_x.length) { addNewMarker(array_x[currentIndex], array_y[currentIndex]); currentIndex++; } else if (currentIndex >= array_x.length) { clearInterval(addMarkerTimer); // 所有标记添加完成后停止定时器 } }, 5000); // 启动动画循环 animate(); } function addNewMarker(x, y) { // 添加新标记,初始状态为红色(isNew: true) var marker = {x: x, y: y, isNew: true}; markers.push(marker); // 2秒后将该标记改为蓝色 setTimeout(function() { marker.isNew = false; }, 2000); } function animate() { if (!isPaused) { draw(); } animationId = requestAnimationFrame(animate); } function draw() { // 清空画布 context.fillStyle = "#000"; context.fillRect(0, 0, canvas.width, canvas.height); // 绘制背景地图 context.drawImage(mapSprite, 0, 0, 954, 267); // 绘制所有标记:根据isNew状态选择红色或蓝色十字 markers.forEach(function(marker) { var cross = marker.isNew ? crossRed : crossBlue; context.drawImage(cross, marker.x, marker.y, 10, 10); }); } // 暂停/播放按钮事件 $('.pause').on('click', function(e) { e.preventDefault(); isPaused = true; }); $('.play').on('click', function(e) { e.preventDefault(); isPaused = false; }); </script>
关键改动说明
- 图片预加载:通过
checkImagesLoaded函数确保所有图片加载完成后再初始化,避免绘制时图片未加载导致的空白问题 - 标记状态数组:
markers数组保存每个标记的坐标和状态,2秒后通过setTimeout修改该标记的isNew属性,重绘时就会自动切换为蓝色 - 单一定时器:只创建一个
addMarkerTimer负责添加新标记,避免原代码中每次draw()都创建新定时器导致的叠加问题 - requestAnimationFrame动画循环:相比直接用定时器重绘,
requestAnimationFrame会和浏览器刷新同步,画面更流畅,也更容易控制暂停/播放
这样修改后,就能完美实现你想要的效果:每隔5秒新增一个红色十字,2秒后该十字自动变为蓝色,始终只有最新的十字是红色,吸引用户注意力。
内容的提问来源于stack exchange,提问作者ranell
相关产品推荐
相关产品推荐

