The acronym SVG stands for Scalable Vector Graphics and refers to graphics that are generated by a geometric specification rather than by pixels, like an image (called rasterized graphics as opposed to the vectorized graphics described here). This example demonstartes how to zoom in on the graph of a line with the mouse wheel.
<!DOCTYPE html> <html> <head> <title>XoaX.net's Javascript</title> <script type="text/javascript" src="GraphOfLine.js"></script> </head> <body onload="fnInitialize()"> </body> </html>
var gqRegion = {mdX0:-10, mdDX:20, mdY0:-10, mdDY:20}; function fnZoom(e) { e.preventDefault(); var dFactor = (e.deltaY > 0.0) ? .1*gqRegion.mdDX : -.1*gqRegion.mdDX; if ((gqRegion.mdDX + dFactor) < 4.0) { gqRegion.mdDX = 4.0; gqRegion.mdDY = 4.0; gqRegion.mdX0 = -2.0; gqRegion.mdY0 = -2.0; } else if ((gqRegion.mdDX + dFactor) > 32.0){ gqRegion.mdDX = 32.0; gqRegion.mdDY = 32.0; gqRegion.mdX0 = -16.0; gqRegion.mdY0 = -16.0; } else { gqRegion.mdDX += dFactor; gqRegion.mdDY += dFactor; gqRegion.mdX0 -= (dFactor/2); gqRegion.mdY0 -= (dFactor/2); } document.getElementById("idFlip").innerHTML = ''; SetVisibleRange(gqRegion.mdX0, gqRegion.mdDX, gqRegion.mdY0, gqRegion.mdDY); CreateLine(2, -4, 1, 'red'); } function fnInitialize() { // Get the body element var qBody = document.getElementsByTagName("body")[0]; // Create a div to catch the mouse events (I will need to do the coordinate conversions) var qMainDiv = document.createElement("div"); qMainDiv.style.backgroundColor = "#ffffff"; qMainDiv.style.width = "600px"; qMainDiv.style.height = "600px"; qMainDiv.addEventListener("wheel", fnZoom, { passive: false }); qBody.appendChild(qMainDiv); // Create the svg element inside the body var qSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); qSvg.setAttribute('width', '600'); qSvg.setAttribute('height', '600'); qMainDiv.appendChild(qSvg); // Add in an element for general definitions var qDefs = document.createElementNS("http://www.w3.org/2000/svg", "defs"); qDefs.setAttribute('id', 'idDefs'); qSvg.appendChild(qDefs); // Add in the markers for the arrowheads var qArrowEnd = document.createElementNS("http://www.w3.org/2000/svg", "marker"); qArrowEnd.setAttribute('id', 'idArrowEnd'); qArrowEnd.setAttribute('markerWidth', '10'); qArrowEnd.setAttribute('markerHeight', '10'); qArrowEnd.setAttribute('refX', '9'); qArrowEnd.setAttribute('refY', '5'); qArrowEnd.setAttribute('orient', 'auto'); qDefs.appendChild(qArrowEnd); var qArrowEndPath = document.createElementNS("http://www.w3.org/2000/svg", "path"); qArrowEndPath.setAttribute('d', 'M 0,0 L10,5 L0,10 Z'); qArrowEndPath.setAttribute('fill', 'context-fill'); qArrowEndPath.setAttribute('stroke', 'context-stroke'); qArrowEnd.appendChild(qArrowEndPath); // The arrow start var qArrowStart = document.createElementNS("http://www.w3.org/2000/svg", "marker"); qArrowStart.setAttribute('id', 'idArrowStart'); qArrowStart.setAttribute('markerWidth', '10'); qArrowStart.setAttribute('markerHeight', '10'); qArrowStart.setAttribute('refX', '0'); qArrowStart.setAttribute('refY', '5'); qArrowStart.setAttribute('orient', 'auto'); qDefs.appendChild(qArrowStart); var qArrowStartPath = document.createElementNS("http://www.w3.org/2000/svg", "path"); qArrowStartPath.setAttribute('d', 'M 10,10 L0,5 L10,0 Z'); qArrowStartPath.setAttribute('fill', 'context-fill'); qArrowStartPath.setAttribute('stroke', 'context-stroke'); qArrowStart.appendChild(qArrowStartPath); // Add in a transformation node var qFlip = document.createElementNS("http://www.w3.org/2000/svg", "g"); qFlip.setAttribute('id', 'idFlip'); // Flip the y values over the x-axis qFlip.setAttribute('transform', 'scale(1,-1)'); qSvg.appendChild(qFlip); // Set the visible range. This will be called again, if scaling is allowed SetVisibleRange(-10, 20, -10, 20); CreateLine(2, -4, 1, 'red'); } function SetVisibleRange(dX0, dDX, dY0, dDY) { gqRegion.mdX0 = dX0; gqRegion.mdDX = dDX; gqRegion.mdY0 = dY0; gqRegion.mdDY = dDY; var qSvg = document.getElementsByTagName("svg")[0]; qSvg.setAttribute('viewBox', dX0+' '+dY0+' '+dDX+' '+dDY); // If the coordinates are set to (x0, dx) and (y0, dy), then after we flip the y-axis the y starts at -(y0 + dy). So, I need to translate by (0, 2y0 + dy). var qFlip = document.getElementById("idFlip"); qFlip.setAttribute('transform', 'translate(0,'+(2*dY0+dDY)+') scale(1,-1)'); // Draw the horizontal grid lines (y = k) // Get the lowest and highest y integers var dLowY = Math.ceil(dY0); // Make sure that the first line isn't at zero var dLeastHoriz = (dLowY == 0.0) ? (dLowY + 1) : dLowY; var dHighY = Math.floor(dY0 + dDY); // Set the horizontal paths, but not y = 0 because that is the x-axis if (dLeastHoriz <= dHighY) { // Add in the horizontal line template for the grid var qHorizPath = document.createElementNS("http://www.w3.org/2000/svg", "path"); qHorizPath.setAttribute('id', 'idHorizPath'); qHorizPath.setAttribute('d', 'M'+dX0+','+dLeastHoriz+' L'+(dX0+dDX)+','+dLeastHoriz); qHorizPath.setAttribute('stroke', 'black'); qHorizPath.setAttribute('stroke-width', '.002'); qFlip.appendChild(qHorizPath); for (var i = dLeastHoriz + 1; i <= dHighY; ++i) { if (i != 0) { var qHorizLine = document.createElementNS("http://www.w3.org/2000/svg", "use"); qHorizLine.setAttribute('href', '#idHorizPath'); qHorizLine.setAttribute('transform', 'translate(0, '+(i - dLeastHoriz)+')'); qFlip.appendChild(qHorizLine); } } } // Add in the x-axis, if needed if (dLowY <= 0.0 && dHighY >= 0.0) { var qAxisX = document.createElementNS("http://www.w3.org/2000/svg", "path"); qAxisX.setAttribute('d', 'M'+dX0+', 0 L'+(dX0+dDX)+', 0'); qAxisX.setAttribute('stroke', 'black'); qAxisX.setAttribute('stroke-width', '.02'); qAxisX.setAttribute('marker-end', 'url(#idArrowEnd)'); qFlip.appendChild(qAxisX); } // Draw the vertical lines (x = h) // Get the lowest and highest x integers var dLowX = Math.ceil(dX0); // Make sure that the first line isn't at zero, the y-axis var dLeastVert = (dLowX == 0.0) ? (dLowX + 1) : dLowX; var dHighX = Math.floor(dX0 + dDX); // Set the horizontal paths, but not y = 0 because that is the x-axis if (dLeastVert <= dHighX) { // Add in the horizontal line template for the grid var qVertPath = document.createElementNS("http://www.w3.org/2000/svg", "path"); qVertPath.setAttribute('id', 'idVertPath'); qVertPath.setAttribute('d', 'M'+dLeastVert+','+dY0+' L'+dLeastVert+','+(dY0+dDY)); qVertPath.setAttribute('stroke', 'black'); qVertPath.setAttribute('stroke-width', '.002'); qFlip.appendChild(qVertPath); for (var i = dLeastVert + 1; i <= dHighX; ++i) { if (i != 0) { var qVertLine = document.createElementNS("http://www.w3.org/2000/svg", "use"); qVertLine.setAttribute('href', '#idVertPath'); qVertLine.setAttribute('transform', 'translate('+(i - dLeastVert)+',0)'); qFlip.appendChild(qVertLine); } } } // Add in the y-axis, if needed if (dLowX <= 0.0 && dHighX >= 0.0) { var qAxisY = document.createElementNS("http://www.w3.org/2000/svg", "path"); qAxisY.setAttribute('d', 'M0,'+dY0+' L0,'+(dY0+dDY)); qAxisY.setAttribute('stroke', 'black'); qAxisY.setAttribute('stroke-width', '.02'); qAxisY.setAttribute('marker-end', 'url(#idArrowEnd)'); qFlip.appendChild(qAxisY); } } function CreateLine(dA, dB, dC, sColor) { // Find the two intersections with the edges, if any: gqRegion = {mdX0:-10, mdDX:20, mdY0:-10, mdDY:20}; // Find the low and high x intersections: y = (C - Ax)/B var daPoints = []; if (dB != 0.0) { var dY = (dC - dA*gqRegion.mdX0)/dB; if ((dY >= gqRegion.mdY0) && (dY <= (gqRegion.mdY0 + gqRegion.mdDY))) { daPoints.push([gqRegion.mdX0, dY]); } dY = (dC - dA*(gqRegion.mdX0 + gqRegion.mdDX))/dB; if (dY >= gqRegion.mdY0 && dY <= (gqRegion.mdY0 + gqRegion.mdDY)) { daPoints.push([gqRegion.mdX0 + gqRegion.mdDX, dY]); } } // Find the low and high y intersections: x = (C - By)/A if (dA != 0.0) { var dX = (dC - dB*gqRegion.mdY0)/dA; if (dX >= gqRegion.mdX0 && dX <= (gqRegion.mdX0 + gqRegion.mdDX)) { daPoints.push([dX, gqRegion.mdY0]); } dX = (dC - dB*(gqRegion.mdY0 + gqRegion.mdDY))/dA; if (dX >= gqRegion.mdX0 && dX <= (gqRegion.mdX0 + gqRegion.mdDX)) { daPoints.push([dX, gqRegion.mdY0 + gqRegion.mdDY]); } } var qFlip = document.getElementById("idFlip"); var qLine = document.createElementNS("http://www.w3.org/2000/svg", "line"); qLine.setAttribute('x1', daPoints[0][0]); qLine.setAttribute('y1', daPoints[0][1]); qLine.setAttribute('x2', daPoints[1][0]); qLine.setAttribute('y2', daPoints[1][1]); qLine.setAttribute('stroke', sColor); qLine.setAttribute('stroke-width', '.02'); qLine.setAttribute('marker-start', 'url(#idArrowStart)'); qLine.setAttribute('marker-end', 'url(#idArrowEnd)'); qFlip.appendChild(qLine); // Add an equation label to the line //<text transform="translate(-2, -2.2) rotate(45) scale(1,-1)" x="0" y="0" font-size=".5" fill="red">x - y = 1</text> var qLabel = document.createElementNS("http://www.w3.org/2000/svg", "text"); // We need three transformations: flip the label, rotate by the slope and translate by the perpendicular and center var sTransformation = 'scale(1,-1)'; // The rotation will be in the range (-90, 90] var dRotation = ((dB == 0.0) ? 90.0 : (Math.atan(-dA/dB)*(180.0/Math.PI))); sTransformation = 'rotate('+dRotation+') '+sTransformation; // The 90 degree counter clockwise perpendicular is (A, B), slope vector is (B, -A) var dL = 1.5*Math.sqrt(dA*dA+dB*dB); sTransformation = 'translate('+((daPoints[0][0] + daPoints[1][0])/2.0 + (dA/dL))+', '+((daPoints[0][1] + daPoints[1][1])/2.0 + (dB/dL))+') '+sTransformation; // Set the label at the midpoint qLabel.setAttribute('transform', sTransformation); qLabel.setAttribute('x', '0'); qLabel.setAttribute('y', '0'); qLabel.setAttribute('text-anchor', 'middle'); qLabel.setAttribute('font-size', '.5'); qLabel.setAttribute('fill', sColor); var sA = (Math.abs(dA) == 1) ? ((dA == 1) ? '' : '-') : dA; var sB = (Math.abs(dB) == 1) ? ((dB == 1) ? ' + ' : ' - ') : ((dB == 1) ? ' + '+ Math.abs(dB) : ' - '+ Math.abs(dB)); qLabel.textContent = sA+'x'+sB+'y = '+dC; qFlip.appendChild(qLabel); }
© 20072024 XoaX.net LLC. All rights reserved.