mirror of
				https://github.com/excalidraw/excalidraw.git
				synced 2025-10-31 02:44:50 +01:00 
			
		
		
		
	feat: add line shape (#371)
* feat: add line shape * fix: align line tool * fix: hitbox bug sw to ne * fix: add stroke width n sloppiness for line * fix: center line inside a panel box * fix: use color as a unique key
This commit is contained in:
		 Bakhtiiar Muzakparov
					Bakhtiiar Muzakparov
				
			
				
					committed by
					
						 Timur Khazamov
						Timur Khazamov
					
				
			
			
				
	
			
			
			 Timur Khazamov
						Timur Khazamov
					
				
			
						parent
						
							42968ef44d
						
					
				
				
					commit
					8db8827c6f
				
			| @@ -33,6 +33,7 @@ const Picker = function({ | |||||||
|             title={color} |             title={color} | ||||||
|             tabIndex={0} |             tabIndex={0} | ||||||
|             style={{ backgroundColor: color }} |             style={{ backgroundColor: color }} | ||||||
|  |             key={color} | ||||||
|           > |           > | ||||||
|             {color === "transparent" ? ( |             {color === "transparent" ? ( | ||||||
|               <div className="color-picker-transparent"></div> |               <div className="color-picker-transparent"></div> | ||||||
|   | |||||||
| @@ -48,3 +48,12 @@ export function getArrowPoints(element: ExcalidrawElement) { | |||||||
|  |  | ||||||
|   return [x1, y1, x2, y2, x3, y3, x4, y4]; |   return [x1, y1, x2, y2, x3, y3, x4, y4]; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export function getLinePoints(element: ExcalidrawElement) { | ||||||
|  |   const x1 = 0; | ||||||
|  |   const y1 = 0; | ||||||
|  |   const x2 = element.width; | ||||||
|  |   const y2 = element.height; | ||||||
|  |  | ||||||
|  |   return [x1, y1, x2, y2]; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -4,7 +4,8 @@ import { ExcalidrawElement } from "./types"; | |||||||
| import { | import { | ||||||
|   getArrowPoints, |   getArrowPoints, | ||||||
|   getDiamondPoints, |   getDiamondPoints, | ||||||
|   getElementAbsoluteCoords |   getElementAbsoluteCoords, | ||||||
|  |   getLinePoints | ||||||
| } from "./bounds"; | } from "./bounds"; | ||||||
|  |  | ||||||
| export function hitTest( | export function hitTest( | ||||||
| @@ -153,6 +154,13 @@ export function hitTest( | |||||||
|       //    / |       //    / | ||||||
|       distanceBetweenPointAndSegment(x, y, x4, y4, x2, y2) < lineThreshold |       distanceBetweenPointAndSegment(x, y, x4, y4, x2, y2) < lineThreshold | ||||||
|     ); |     ); | ||||||
|  |   } else if (element.type === "line") { | ||||||
|  |     const [x1, y1, x2, y2] = getLinePoints(element); | ||||||
|  |     // The computation is done at the origin, we need to add a translation | ||||||
|  |     x -= element.x; | ||||||
|  |     y -= element.y; | ||||||
|  |  | ||||||
|  |     return distanceBetweenPointAndSegment(x, y, x1, y1, x2, y2) < lineThreshold; | ||||||
|   } else if (element.type === "text") { |   } else if (element.type === "text") { | ||||||
|     const [x1, y1, x2, y2] = getElementAbsoluteCoords(element); |     const [x1, y1, x2, y2] = getElementAbsoluteCoords(element); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -76,7 +76,7 @@ export function handlerRectangles( | |||||||
|     8 |     8 | ||||||
|   ]; // se |   ]; // se | ||||||
|  |  | ||||||
|   if (element.type === "arrow") { |   if (element.type === "arrow" || element.type === "line") { | ||||||
|     return { |     return { | ||||||
|       nw: handlers.nw, |       nw: handlers.nw, | ||||||
|       se: handlers.se |       se: handlers.se | ||||||
|   | |||||||
| @@ -2,7 +2,8 @@ export { newElement, duplicateElement } from "./newElement"; | |||||||
| export { | export { | ||||||
|   getElementAbsoluteCoords, |   getElementAbsoluteCoords, | ||||||
|   getDiamondPoints, |   getDiamondPoints, | ||||||
|   getArrowPoints |   getArrowPoints, | ||||||
|  |   getLinePoints | ||||||
| } from "./bounds"; | } from "./bounds"; | ||||||
|  |  | ||||||
| export { handlerRectangles } from "./handlerRectangles"; | export { handlerRectangles } from "./handlerRectangles"; | ||||||
|   | |||||||
| @@ -1,6 +1,10 @@ | |||||||
| import { ExcalidrawElement } from "../element/types"; | import { ExcalidrawElement } from "../element/types"; | ||||||
| import { isTextElement } from "../element/typeChecks"; | import { isTextElement } from "../element/typeChecks"; | ||||||
| import { getDiamondPoints, getArrowPoints } from "../element/bounds"; | import { | ||||||
|  |   getDiamondPoints, | ||||||
|  |   getArrowPoints, | ||||||
|  |   getLinePoints | ||||||
|  | } from "../element/bounds"; | ||||||
| import { RoughCanvas } from "roughjs/bin/canvas"; | import { RoughCanvas } from "roughjs/bin/canvas"; | ||||||
| import { Drawable } from "roughjs/bin/core"; | import { Drawable } from "roughjs/bin/core"; | ||||||
|  |  | ||||||
| @@ -118,6 +122,22 @@ export function renderElement( | |||||||
|     (element.shape as Drawable[]).forEach(shape => rc.draw(shape)); |     (element.shape as Drawable[]).forEach(shape => rc.draw(shape)); | ||||||
|     context.globalAlpha = 1; |     context.globalAlpha = 1; | ||||||
|     return; |     return; | ||||||
|  |   } else if (element.type === "line") { | ||||||
|  |     const [x1, y1, x2, y2] = getLinePoints(element); | ||||||
|  |     const options = { | ||||||
|  |       stroke: element.strokeColor, | ||||||
|  |       strokeWidth: element.strokeWidth, | ||||||
|  |       roughness: element.roughness, | ||||||
|  |       seed: element.seed | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     if (!element.shape) { | ||||||
|  |       element.shape = generator.line(x1, y1, x2, y2, options); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     context.globalAlpha = element.opacity / 100; | ||||||
|  |     rc.draw(element.shape as Drawable); | ||||||
|  |     context.globalAlpha = 1; | ||||||
|   } else if (isTextElement(element)) { |   } else if (isTextElement(element)) { | ||||||
|     context.globalAlpha = element.opacity / 100; |     context.globalAlpha = element.opacity / 100; | ||||||
|     const font = context.font; |     const font = context.font; | ||||||
|   | |||||||
| @@ -18,7 +18,8 @@ export const hasStroke = (elements: readonly ExcalidrawElement[]) => | |||||||
|       (element.type === "rectangle" || |       (element.type === "rectangle" || | ||||||
|         element.type === "ellipse" || |         element.type === "ellipse" || | ||||||
|         element.type === "diamond" || |         element.type === "diamond" || | ||||||
|         element.type === "arrow") |         element.type === "arrow" || | ||||||
|  |         element.type === "line") | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
| export const hasText = (elements: readonly ExcalidrawElement[]) => | export const hasText = (elements: readonly ExcalidrawElement[]) => | ||||||
|   | |||||||
| @@ -55,6 +55,22 @@ export const SHAPES = [ | |||||||
|       </svg> |       </svg> | ||||||
|     ), |     ), | ||||||
|     value: "text" |     value: "text" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     icon: ( | ||||||
|  |       // custom | ||||||
|  |       <svg viewBox="0 0 6 6"> | ||||||
|  |         <line | ||||||
|  |           x1="0" | ||||||
|  |           y1="3" | ||||||
|  |           x2="6" | ||||||
|  |           y2="3" | ||||||
|  |           stroke="black" | ||||||
|  |           strokeLinecap="round" | ||||||
|  |         /> | ||||||
|  |       </svg> | ||||||
|  |     ), | ||||||
|  |     value: "line" | ||||||
|   } |   } | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user