canvas绘制可缩放的室内地图和路径
生活随笔
收集整理的這篇文章主要介紹了
canvas绘制可缩放的室内地图和路径
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
最近一直在寫canvas繪制室內地圖和路徑這個功能,大致聊一下這個功能講了什么。
具體是需要用canvas將室內地圖繪制出來(這個不難,canvas教程有),并且地圖需要能進行放大縮小,同時將地圖上的路徑進行展示,同樣需要能放大縮小。
其實不能看出,就是將地圖和路徑進行同時的放大縮小。
我之前的思路是(之前沒有要求縮放),創建canvas,然后設置背景(背景就是地圖),然后將后端的路徑數據展示到前端背景上,具體的實現,之前的博客有說明,可以翻翻看。
之后添加一個功能就是要求能將地圖和路徑能進行縮放,那么再將地圖作為背景就不能實現縮放,所以我就使用canvas先繪制一個圖片,先實現圖片縮放,再在圖片縮放的同時將路徑數據放入到圖片中,實現在繪制圖片的同時,順帶加上路徑數據。這是我的思路,因為沒系統的接觸過canvas,不知道還有沒有別的思路和方法,總感覺我的還是有問題。
具體流程:(html + vue + django)
HTML: <canvas id="scaleDragCanvas" width="1276" height="800"></canvas>vue、js:var img_path = res.data['img_path']; // 不用太在意,圖片路徑var piece = res.data['loc_data']; // 不用太在意,路徑數據_this.rountData = res.data['rount_data']; // 不用太在意,路線數據var canvas, context;var showTips = null, originalPos = null;var initWidth = 0, initHeight = 0;var img, imgX = 0, imgY = 0, imgScale = 1, scaleRate = 1, scaleRatey = 1; // imgScale 縮放比例 scaleRate 圖片比例尺 imgX 圖片左上角坐標var MINIMUM_SCALE = 1 ,pos = {}, posl = {}, dragging = false;var rount_len = res.data['loc_data'].length;(function int() { // 縮放主函數canvas = document.getElementById('scaleDragCanvas'); // 畫布對象context = canvas.getContext('2d'); // 畫布顯示二維圖片loadImg();canvasEventsInit(); // 轉換坐標})();function stroke() { // 縮放路徑var color_list = ['rgb(255,0,0)', 'rgb(255,255,0)', 'rgb(0,255,0)', 'rgb(0,0,255)', 'rgb(255,0,255)', 'rgb(0,0,0)'];for (var j = 0; j < rount_len; j++){piece = res.data['loc_data'][j];context.strokeStyle = color_list[j];for (var i = 0; i < piece.length; i++) {context.beginPath();context.moveTo((piece[i].startX)*(imgScale),(piece[i].startY)*(imgScale));context.lineTo((piece[i].endX)*(imgScale), (piece[i].endY)*(imgScale));context.stroke();}}}function strokemove() { // 移動路徑var color_list = ['rgb(255,0,0)', 'rgb(255,255,0)', 'rgb(0,255,0)', 'rgb(0,0,255)', 'rgb(255,0,255)', 'rgb(0,0,0)'];for (var j = 0; j < rount_len; j++){piece = res.data['loc_data'][j];context.strokeStyle = color_list[j];for (var i = 0; i < piece.length; i++) {context.beginPath();context.moveTo(((piece[i].startX)*(imgScale))+imgX,((piece[i].startY)*(imgScale))+imgY);context.lineTo(((piece[i].endX)*(imgScale))+imgX, ((piece[i].endY)*(imgScale))+imgY);context.stroke();}}}function loadImg() { // 畫圖主函數img = new Image();img.onload = function () {scaleRate = img.width / canvas.width; // 讓長寬保持相同縮放比例scaleRatey = img.height / canvas.height;initWidth = img.width * scaleRate;initHeight = img.height * scaleRatey;console.log("initWidth=" + initWidth + ",initHeight=" + initHeight + ", scaleRate=" + scaleRate + ", scaleRatey=" + scaleRatey);drawImage();};img.src ='/media/maps/' + img_path;}function drawImage() { // 畫圖副函數context.clearRect(0, 0, canvas.width, canvas.height); // 清空給定矩形內的指定像素// 保證 imgX 在 [img.width*(1-imgScale),0] 區間內///**if(imgX<img.width*(1-imgScale) /scaleRate) {imgX = img.width*(1-imgScale)/scaleRate ;}else if(imgX>0) {imgX=0}// 保證 imgY 在 [img.height*(1-imgScale),0] 區間內if(imgY<img.height*(1-imgScale)/scaleRatey) {imgY = img.height*(1-imgScale)/scaleRatey;}else if(imgY>0) {imgY=0}//*/context.drawImage(img, //規定要使用的圖像、畫布或視頻。0, 0, //開始剪切的 xy 坐標位置。initWidth, initHeight, //被剪切圖像的高度。imgX, imgY,//在畫布上放置圖像的 x 、y坐標位置。img.width * imgScale, img.height * imgScale //要使用的圖像的寬度、高度);stroke();}/*事件注冊*/function canvasEventsInit() {canvas.onmousedown = function (event) { // 鼠標按下if(showTips == null){dragging = true;pos = windowToCanvas(event.clientX, event.clientY); // 坐標轉換,將窗口坐標轉換成canvas的坐標}};canvas.onmousemove = function (evt) { // 鼠標移動if(showTips != null){// 測距功能if(showTips){showPosTips(originalPos.x, originalPos.y, evt);}}else{if(dragging){posl = windowToCanvas(evt.clientX, evt.clientY);let x = posl.x - pos.x, y = posl.y - pos.y;imgX += x;imgY += y;pos = JSON.parse(JSON.stringify(posl));context.clearRect(0, 0, canvas.width, canvas.height); // 清空給定矩形內的指定像素// 保證 imgX 在 [img.width*(1-imgScale),0] 區間內///**if(imgX<img.width*(1-imgScale) /scaleRate) {imgX = img.width*(1-imgScale)/scaleRate ;}else if(imgX>0) {imgX=0}// 保證 imgY 在 [img.height*(1-imgScale),0] 區間內if(imgY<img.height*(1-imgScale)/scaleRatey) {imgY = img.height*(1-imgScale)/scaleRatey;}else if(imgY>0) {imgY=0}//*/context.drawImage(img, //規定要使用的圖像、畫布或視頻。0, 0, //開始剪切的 xy 坐標位置。initWidth, initHeight, //被剪切圖像的高度。imgX, imgY,//在畫布上放置圖像的 x 、y坐標位置。img.width * imgScale, img.height * imgScale //要使用的圖像的寬度、高度);strokemove();}else{let pos3 = windowToCanvas(evt.clientX, evt.clientY);if(context.isPointInPath(pos3.x, pos3.y)){console.log("進入區域");}}}};canvas.onmouseup = function (evt) { // 鼠標抬起if(showTips != null){if(!showTips){showTips = true;}else{showTips = null;}}else{dragging = false;}};canvas.onmousewheel = canvas.onwheel = function (event) { //滾輪放大縮小let pos = windowToCanvas (event.clientX, event.clientY);event.wheelDelta = event.wheelDelta ? event.wheelDelta : (event.deltalY * (-40)); //獲取當前鼠標的滾動情況let newPos = {x:((pos.x-imgX)/imgScale).toFixed(2) , y:((pos.y-imgY)/imgScale).toFixed(2)};if (event.wheelDelta > 0) {// 放大imgScale +=0.1;imgX = 0;imgY = 0;} else {// 縮小imgScale -=0.1;if(imgScale<MINIMUM_SCALE) {//最小縮放1imgScale = MINIMUM_SCALE;}imgX = 0;imgY = 0;}drawImage(); //重新繪制圖片};canvas.addEventListener('dblclick', function (event) { // 雙擊let pos = windowToCanvas(event.clientX,event.clientY);//實際點的坐標let isX = 0;let isY = 0;//實際選中坐標 肯定 = 點擊的坐標 + canvas的坐標(必須為-數)let temp_canvas_x = imgX-imgX*2;let temp_canvas_y = imgY-imgY*2;scaleRate = img.width / canvas.width; // 讓長寬保持相同縮放比例scaleRatey = img.height / canvas.height;if(imgScale === 1){ //原始尺寸isX = (pos.x+temp_canvas_x)*scaleRate;isY = (pos.y+temp_canvas_y)*scaleRatey;axios.get('/api/loc/', {params: {'loc_x': isX, 'loc_y': isY}})} else {if (imgScale > 1) { //被放大了,實際坐標需要/isX = ((pos.x + temp_canvas_x) / imgScale)*scaleRate;isY = ((pos.y + temp_canvas_y) / imgScale)*scaleRatey;console.log(isX, isY);axios.get('/api/loc/', {params: {'loc_x': isX, 'loc_y': isY}})} else { //被縮小了,坐標點放大isX = ((pos.x + temp_canvas_x) * (1 / imgScale))*scaleRate;isY = ((pos.y + temp_canvas_y) * (1 / imgScale)*scaleRatey);console.log(isX, isY);axios.get('/api/loc/', {params: {'loc_x': isX, 'loc_y': isY}})}}});}function showPosTips(x, y, evt){drawImage();context.beginPath();context.stokeStyle="#f00";context.moveTo(x, y);let temp = windowToCanvas(evt.clientX, evt.clientY);context.lineTo(temp.x, temp.y);context.stroke();document.getElementById("mouseTip").innerHTML = temp.x + "," + temp.y;document.getElementById("mouseTip").style.left = evt.clientX + 'px';document.getElementById("mouseTip").style.top = evt.clientY + 'px';}/*坐標轉換*/function windowToCanvas(x,y) {var box = canvas.getBoundingClientRect();//console.log( "x=" + (x - box.left - (box.width - canvas.width) / 2) + ",y=" + (y - box.top - (box.height - canvas.height) / 2));//這個方法返回一個矩形對象,包含四個屬性:left、top、right和bottom。分別表示元素各邊與頁面上邊和左邊的距離return {x: x - box.left - (box.width - canvas.width) / 2,y: y - box.top - (box.height - canvas.height) / 2}; } django: # api @csrf_exempt def get_loc(request):"""保存地圖路徑到數據庫"""json_str = request.body.decode()req_data = json.loads(json_str)req_data = req_data['loc_data'] # 一個大的列表req_len = len(req_data)for i in range(req_len):map_name = ((req_data[i]['map_name']).split('.')[0]) + '.jpg'rount_index = req_data[i]['rount_index']loc = req_data[i]['loc'] # 一個列表loc_len = len(loc)for i in range(loc_len):loc_seq = loc[i]['loc_seq']loc_x = loc[i]['loc_x']loc_y = loc[i]['loc_y']try:map = Map.objects.get(name=map_name)Location.objects.create(loc2_index=rount_index, loc2_x=loc_x, loc2_y=loc_y, loc2_seq=loc_seq, mid_id=map.id)except Exception as e:print(e)return HttpResponse('not ok')return HttpResponse('is ok!!!')# api @csrf_exempt def get_map(request):"""保存地圖"""car_index = request.POST.get('car_index')img = request.FILES.get('img')map_name = request.POST.get('map_name')if img is None:return HttpResponse('img is not ok')try:robot = Robot.objects.get(car_index=car_index)Map.objects.create(name=map_name, rid_id=robot.id, img=img)except Exception as e:print(e)return HttpResponse('mysql not ok')new_img_path, new_img_name = pgm_tojpg(map_name)Map.objects.filter(name=map_name).update(name=new_img_name, img=new_img_path)return HttpResponse('is ok!!!')def pgm_tojpg(img_name):"""pgm轉換成jpg"""img = Image.open(MEDIA_ROOT + '/maps/' + img_name)img_name = img_name.split('.')[0]new_img_name = img_name + '.jpg'new_img_path = 'maps/' + img_name + '.jpg'img.save(MEDIA_ROOT + '/maps/' + img_name + '.jpg')return new_img_path, new_img_namedef get_mapdetail(request):"""獲取地圖詳情: 坐標,路線"""map_name = request.GET.get('map_name')m = Map.objects.get(name=map_name)img = Image.open(MEDIA_ROOT + '/maps/' + map_name)wimg = img.widthhimg = img.heightrount = m.location_set.values('loc2_index').distinct()list3 = []color_list = [{"rgb(255,0,0)": "紅"}, {"rgb(255,255,0)": "黃"}, {"rgb(0,255,0)": "青"}, {"rgb(0,0,255)": "藍"}, {"rgb(255,0,255)": "紫"}, {"rgb(0,0,0)": "黑"}]for r in rount:list3.append({'rount_index': (r['loc2_index']).split('rount_')[1],'rount_name': r['loc2_index'],'rount_color': list(color_list[int((r['loc2_index']).split('rount_')[1])-1].values())[0]})print(list3)rount_count = rount.count() # 多少條路線context = {}list1 = []for i in range(rount_count):list2 = []rount_index = 'rount_' + str(i+1)L = m.location_set.filter(loc2_index=rount_index)l_len = len(L)if l_len <= 1:print('只有一個點')if wimg >= 1276 and himg >= 800:for i in range(l_len - 1):list2.append({'startX': float(L[i].loc2_x) / (wimg / 1276), # 5083 6597'startY': float(L[i].loc2_y) / (himg / 800),'endX': float(L[i + 1].loc2_x) / (wimg / 1276),'endY': float(L[i + 1].loc2_y) / (himg / 800)})list1.append(list2)else:for i in range(l_len - 1):list2.append({'startX': float(L[i].loc2_x) * (wimg / 1276),'startY': float(L[i].loc2_y) * (himg / 800),'endX': float(L[i + 1].loc2_x) * (wimg / 1276),'endY': float(L[i + 1].loc2_y) * (himg / 800)})list1.append(list2)context['loc_data'] = list1context['img_path'] = map_namecontext['rount_data'] = list3return JsonResponse(data=context)# api @csrf_exempt def set_loc(request):"""傳遞坐標給小車"""loc_x = request.GET.get('loc_x')loc_y = request.GET.get('loc_y')context = {}context['loc_x'] = loc_xcontext['loc_y'] = loc_yprint(context)res = upload_rount(context)return JsonResponse(res, safe=False)# api @csrf_exempt def set_rount(request):"""傳遞路線"""rount_index = request.GET.get('rount_index')res = upload_rount(rount_index)return JsonResponse(res, safe=False)總結
以上是生活随笔為你收集整理的canvas绘制可缩放的室内地图和路径的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2023年值得参加的数学建模竞赛介绍
- 下一篇: Hadoop 三节点集群搭建