如何用Google Maps的Drawing Manager实现类似Esri工具的曲线及贝塞尔曲线绘制?
Got it, let's figure out how to replicate that Esri drawing toolbar's curve and Bezier curve functionality using Google Maps' Drawing Manager. The default Drawing Manager doesn't ship with these tools out of the box, but we can build them ourselves with some custom code—here's a step-by-step breakdown:
1. Set Up the Base Map & Drawing Manager
First, we'll initialize our map and the default Drawing Manager with standard tools (markers, polylines, etc.). We'll add our custom curve tools to this setup later.
// Initialize the map const map = new google.maps.Map(document.getElementById("map"), { center: { lat: -34.397, lng: 150.644 }, zoom: 8, }); // Initialize Drawing Manager with default tools const drawingManager = new google.maps.drawing.DrawingManager({ drawingMode: null, drawingControl: true, drawingControlOptions: { position: google.maps.ControlPosition.TOP_CENTER, drawingModes: [ google.maps.drawing.OverlayType.MARKER, google.maps.drawing.OverlayType.POLYLINE, google.maps.drawing.OverlayType.POLYGON, ], }, }); drawingManager.setMap(map);
2. Add Custom UI Buttons for Curve Tools
The default Drawing Manager doesn't include curve/Bezier options, so we'll create custom buttons and add them to the existing drawing control container.
// Grab the existing drawing control container const drawingControlContainer = document.querySelector(".gm-drawing-control"); // Create Freehand Curve button const freehandBtn = document.createElement("button"); freehandBtn.textContent = "Freehand Curve"; freehandBtn.className = "gm-ui-hover-effect"; drawingControlContainer.appendChild(freehandBtn); // Create Bezier Curve button const bezierBtn = document.createElement("button"); bezierBtn.textContent = "Bezier Curve"; bezierBtn.className = "gm-ui-hover-effect"; drawingControlContainer.appendChild(bezierBtn);
3. Implement Freehand Curve Drawing
For freehand curves (like drawing a smooth line with your mouse), we'll listen to mouse events to collect points as the user drags, then connect those points with a polyline.
let isDrawingFreehand = false; let freehandPath = []; let freehandPolyline = null; // Toggle freehand drawing mode on button click freehandBtn.addEventListener("click", () => { isDrawingFreehand = !isDrawingFreehand; freehandBtn.style.backgroundColor = isDrawingFreehand ? "#4285F4" : ""; freehandBtn.style.color = isDrawingFreehand ? "white" : ""; if (isDrawingFreehand) { map.addListener("mousedown", startFreehandDraw); } else { google.maps.event.clearListeners(map, "mousedown"); } }); // Start collecting points on mouse down function startFreehandDraw(e) { freehandPath = [e.latLng]; freehandPolyline = new google.maps.Polyline({ path: freehandPath, map: map, strokeColor: "#00FF00", strokeOpacity: 1.0, strokeWeight: 2, }); map.addListener("mousemove", updateFreehandDraw); map.addListener("mouseup", endFreehandDraw); } // Update the curve as the user moves the mouse function updateFreehandDraw(e) { freehandPath.push(e.latLng); freehandPolyline.setPath(freehandPath); } // Finalize the freehand curve when mouse is released function endFreehandDraw() { isDrawingFreehand = false; freehandBtn.style.backgroundColor = ""; freehandBtn.style.color = ""; google.maps.event.clearListeners(map, "mousemove"); google.maps.event.clearListeners(map, "mouseup"); }
4. Implement Bezier Curve Drawing
Google Maps doesn't support native Bezier curves, so we'll approximate them by calculating a dense set of points along the Bezier path and connecting them with a polyline. We'll use quadratic Bezier (needs 3 points: start, control, end) for simplicity.
let isDrawingBezier = false; let bezierPoints = []; let previewPolyline = null; // Toggle Bezier drawing mode on button click bezierBtn.addEventListener("click", () => { isDrawingBezier = !isDrawingBezier; bezierPoints = []; bezierBtn.style.backgroundColor = isDrawingBezier ? "#4285F4" : ""; bezierBtn.style.color = isDrawingBezier ? "white" : ""; if (isDrawingBezier) { map.addListener("mousedown", startBezierDraw); } else { google.maps.event.clearListeners(map, "mousedown"); if (previewPolyline) previewPolyline.setMap(null); } }); // Collect Bezier points on mouse click function startBezierDraw(e) { const point = e.latLng; bezierPoints.push(point); // Add a marker to indicate the point new google.maps.Marker({ position: point, map: map, icon: { url: bezierPoints.length === 1 ? "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Ccircle%20cx%3D%2210%22%20cy%3D%2210%22%20r%3D%228%22%20fill%3D%22%234285F4%22%20stroke%3D%22white%22%20stroke-width%3D%222%22%2F%3E%3C%2Fsvg%3E" : "data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Ccircle%20cx%3D%228%22%20cy%3D%228%22%20r%3D%226%22%20fill%3D%22%23FBBC05%22%20stroke%3D%22white%22%20stroke-width%3D%222%22%2F%3E%3C%2Fsvg%3E", anchor: new google.maps.Point(bezierPoints.length === 1 ? 10 : 8, bezierPoints.length === 1 ? 10 : 8), }, }); // Handle different stages of Bezier drawing if (bezierPoints.length === 1) { // First point: listen for mouse move to preview the control line map.addListener("mousemove", previewBezierControl); map.addListener("mouseup", addBezierControlPoint); } else if (bezierPoints.length === 3) { // All 3 points collected: generate and draw the final Bezier curve const curvePoints = generateQuadraticBezier(bezierPoints[0], bezierPoints[1], bezierPoints[2]); new google.maps.Polyline({ path: curvePoints, map: map, strokeColor: "#FF0000", strokeOpacity: 1.0, strokeWeight: 2, }); // Reset drawing state endBezierDraw(); } } // Preview the control line while moving the mouse function previewBezierControl(e) { if (previewPolyline) previewPolyline.setMap(null); previewPolyline = new google.maps.Polyline({ path: [bezierPoints[0], e.latLng], map: map, strokeColor: "#AAAAAA", strokeOpacity: 0.7, strokeWeight: 1, dashStyle: "dash", }); } // Add the control point on mouse up function addBezierControlPoint(e) { google.maps.event.clearListeners(map, "mousemove"); google.maps.event.clearListeners(map, "mouseup"); // Now listen for the end point map.addListener("mousemove", previewBezierCurve); map.addListener("mouseup", addBezierEndPoint); } // Preview the Bezier curve while moving to the end point function previewBezierCurve(e) { const curvePoints = generateQuadraticBezier(bezierPoints[0], bezierPoints[1], e.latLng); if (previewPolyline) previewPolyline.setMap(null); previewPolyline = new google.maps.Polyline({ path: curvePoints, map: map, strokeColor: "#FF0000", strokeOpacity: 0.7, strokeWeight: 2, }); } // Add the end point and finalize the curve function addBezierEndPoint(e) { bezierPoints.push(e.latLng); google.maps.event.clearListeners(map, "mousemove"); google.maps.event.clearListeners(map, "mouseup"); } // Calculate points for a quadratic Bezier curve function generateQuadraticBezier(start, control, end) { const points = []; const steps = 50; // More steps = smoother curve (adjust as needed) for (let t = 0; t <= 1; t += 1/steps) { const lat = Math.pow(1-t, 2)*start.lat() + 2*(1-t)*t*control.lat() + Math.pow(t,2)*end.lat(); const lng = Math.pow(1-t, 2)*start.lng() + 2*(1-t)*t*control.lng() + Math.pow(t,2)*end.lng(); points.push(new google.maps.LatLng(lat, lng)); } return points; } // Reset Bezier drawing state function endBezierDraw() { isDrawingBezier = false; bezierBtn.style.backgroundColor = ""; bezierBtn.style.color = ""; google.maps.event.clearListeners(map, "mousedown"); if (previewPolyline) previewPolyline.setMap(null); }
Key Notes
- Google Maps doesn't have native Bezier curve support, so we approximate it with a dense polyline of calculated points. Tweak the
stepsvariable ingenerateQuadraticBezierto balance smoothness and performance. - You can customize the button styles, marker icons, and curve colors to match Esri's toolbar if needed.
- For cubic Bezier curves (with two control points), just extend the logic to collect four points and update the
generateQuadraticBezierfunction to use the cubic Bezier formula.
内容的提问来源于stack exchange,提问作者mustafa.salaheldin

