paperjs

Toggling multiple tools on Paper.js


Can someone show me a working example (JSFiddle or otherwise) of how to have two tools on Paper.js that a user can click on to draw different shapes, say one for circles and one for squares? Thanks!


Solution

  • You've got at least a couple of options here,

    1. Activate Tool from paper.tools

    Paper.js allows you to activate a Tool by calling tool.activate(), which causes only that particular Tool to receive Tool events, such as mousedown, mousedrag etc ...

    When you create a new Tool via new paper.Tool(), that Tool is added in paper.tools so you can lookup for the Tool within that Array and call tool.activate() on it.

    An example:

    window.onload = () => {
      // Setup Paper
    
      paper.setup(document.querySelector('canvas'))
        
      // Find a Tool in `paper.tools` and activate it
    
      const activateTool = name => {
        const tool = paper.tools.find(tool => tool.name === name)
        tool.activate()
      }
    
      // Tool Path, draws paths on mouse-drag.
      // Note: Wrap each Tool in an IIFE to avoid polluting the 
      //       global scope with variables related with that Tool.
    
      ;(() => {
        const tool = new paper.Tool()
        tool.name = 'toolPath'
    
        let path
    
        tool.onMouseDown = function(event) {
          path = new paper.Path()
          path.strokeColor = '#424242'
          path.strokeWidth = 4
          path.add(event.point)
        }
    
        tool.onMouseDrag = function(event) {
          path.add(event.point)
        }
      })()
    
      // Tool Circle, draws a 30px circle on mousedown
    
      ;(() => {
        const tool = new paper.Tool()
        tool.name = 'toolCircle'
    
        let path
    
        tool.onMouseDown = function(event) {
          path = new paper.Path.Circle({
            center: event.point,
            radius: 30,
            fillColor: '#9C27B0'
          })
        }
      })()
    
      // Attach click handlers for Tool activation on all
      // DOM buttons with class '.tool-button'
    
      document.querySelectorAll('.tool-button').forEach(toolBtn => {
        toolBtn.addEventListener('click', e => {
          activateTool(e.target.getAttribute('data-tool-name'))
        })
      })
    }
    html,
    body,
    canvas {
      width: 100%;
      height: 100%;
      margin: 0;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.11.5/paper-core.js"></script>
    
    <button 
     class="tool-button"
     data-tool-name="toolPath">
     Draw Paths
    </button>
    
    <button 
     class="tool-button"
     data-tool-name="toolCircle">
     Stamp Circles
    </button>
    
    <canvas resize></canvas>

    2. Create a ToolStack Class

    However, I find it far more practical to create a ToolStack Class since it allows me to add additional methods later on, i.e isToolActive(), onToolSelect() (for adding is-active classes to DOM tool buttons) etc..

    The ToolStack should then implement various methods for handling your Tools, the first and foremost being an activateTool method, that will lookup for a Tool by name and call it's tool.activate() method.

    An example:

    window.onload = () => {
      // Setup Paper
    
      paper.setup(document.querySelector('canvas'))
    
      // Toolstack
    
      class ToolStack {
        constructor(tools) {
          this.tools = tools.map(tool => tool())
        }
    
        activateTool(name) {
          const tool = this.tools.find(tool => tool.name === name)
          tool.activate()
        }
    
        // add more methods here as you see fit ...
      }
    
      // Tool Path, draws paths on mouse-drag
    
      const toolPath = () => {
        const tool = new paper.Tool()
        tool.name = 'toolPath'
    
        let path
    
        tool.onMouseDown = function(event) {
          path = new paper.Path()
          path.strokeColor = '#424242'
          path.strokeWidth = 4
          path.add(event.point)
        }
    
        tool.onMouseDrag = function(event) {
          path.add(event.point)
        }
    
        return tool
      }
    
      // Tool Circle, draws a 30px circle on mousedown
    
      const toolCircle = () => {
        const tool = new paper.Tool()
        tool.name = 'toolCircle'
    
        let path
    
        tool.onMouseDown = function(event) {
          path = new paper.Path.Circle({
            center: event.point,
            radius: 30,
            fillColor: '#9C27B0'
          })
        }
    
        return tool
      }
    
      // Construct a Toolstack, passing your Tools
    
      const toolStack = new ToolStack([toolPath, toolCircle])
    
      // Activate a certain Tool
    
      toolStack.activateTool('toolPath')
    
      // Attach click handlers for Tool activation on all
      // DOM buttons with class '.tool-button'
    
      document.querySelectorAll('.tool-button').forEach(toolBtn => {
        toolBtn.addEventListener('click', e => {
          toolStack.activateTool(e.target.getAttribute('data-tool-name'))
        })
      })
    }
    html,
    body,
    canvas {
      width: 100%;
      height: 100%;
      margin: 0;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.11.5/paper-core.js"></script>
    
    <button 
     class="tool-button"
     data-tool-name="toolPath">
     Draw Paths
    </button>
    
    <button 
     class="tool-button"
     data-tool-name="toolCircle">
     Stamp Circles
    </button>
    
    <canvas resize></canvas>