X-Git-Url: https://jasonwoof.com/gitweb/?p=crayon_mockup.git;a=blobdiff_plain;f=main.coffee;fp=main.coffee;h=270b3cd7a08f511b21773a5023b342d10a9b9207;hp=0000000000000000000000000000000000000000;hb=2d902775d3b9f916b5a38ac8874a734ca586e6b3;hpb=5a5d608f1cf57587e6acf1f83f98c1ae5aa48938 diff --git a/main.coffee b/main.coffee new file mode 100644 index 0000000..270b3cd --- /dev/null +++ b/main.coffee @@ -0,0 +1,281 @@ +# Copyright 2015 Jason Woofenden +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program. If not, see . + + +# settings +width = 800 +height = 600 +supply_height = 96 +CLICK_FUZ = 10 # this far away from things is close enough to be "clicked on" +PROX_MAX = CLICK_FUZ * CLICK_FUZ + +# constants +STYLE_NORMAL = 0 +STYLE_SELECTED = 1 +STYLE_HOVER = 2 +STYLE_EDITING = 3 +STYLE_DRAGGING = 4 + +STYLE_TO_CLASS = [ + "normal" + "selected" + "hover" + "editing" + "dragging" +] + +set_style_class = (args) -> + args.el.setAttribute 'class', "#{args.class} #{STYLE_TO_CLASS[args.style]}" + +# json (compiled to javascript and minified) is ~8% smaller than the resulting xml +json_to_svg = (json) -> + for tag, attrs of json + el = document.createElementNS 'http://www.w3.org/2000/svg', tag + for k, v of attrs + if k is 'children' + for child in v + el.appendChild json_to_svg child + else if k is 'contents' + el.appendChild document.createTextNode v + else + el.setAttribute k, v + return el + +next_widget_id = 0 +# public vars: x, y, width, height, el +class Visible + # required args: svg + constructor: (args) -> + @id = next_widget_id + next_widget_id += 1 + @svg = args.svg + @x = args.x ? 1 + @y = args.y ? 1 + @width = args.width ? 50 + @height = args.height ? 34 + @style = args.style ? STYLE_NORMAL + destruct: -> + move: (args) -> + @x = args.x + @y = args.y + proximity: (xy) -> # return the square of the distance to your visible bits + return PROX_MAX + 1 + set_style: (style) -> + @style = style + +class Control extends Visible + +class Widget extends Visible + #sub-classes are expected to implement all of these: + clone: -> + return new Widget @ + controls: -> # create controls, return them + return [] + hide_controls: -> + +class RectWidget extends Widget + constructor: (args) -> + super args + @css_class = 'box' + @el = json_to_svg rect: + x: @x + 1 + y: @y + 1 + width: @width - 2 + height: @height - 2 + class: 'box normal' + @svg.appendChild @el + destruct: -> + super() + if @el? + @svg.removeChild @el + clone: -> + return new RectWidget @ + set_style: (style) -> + super style + set_style_class el: @el, class: 'box', style: style + move: (args) -> + super args + @el.setAttribute 'x', @x + 1 + @el.setAttribute 'y', @y + 1 + proximity: (xy) -> # return the square of the distance to your visible bits + x = xy.x + y = xy.y + prox = PROX_MAX + 1 + in_x = false + in_y = false + if x > @x - CLICK_FUZ and x < @x + @width + CLICK_FUZ + in_x = true + if y < @y + @height / 2 + new_prox = @y - y + else + new_prox = @y + @height - y + new_prox *= new_prox + if new_prox < prox + prox = new_prox + if y > @y - CLICK_FUZ and y < @y + @height + CLICK_FUZ + in_y = true + if x < @x + @width / 2 + new_prox = @x - x + else + new_prox = @x + @width - x + new_prox *= new_prox + if new_prox < prox + prox = new_prox + if in_x and in_y and prox > PROX_MAX + prox = PROX_MAX - 1 + return prox + controls: -> # create controls, return them + return [] + hide_controls: -> + +# called automatically on domcontentloaded +init = -> + svg_offset = null + $container = $ '.crayon_mockup' + svg = json_to_svg svg: width: width, height: height, viewBox: "0 0 #{width} #{height}" + $svg = $ svg + $container.append $svg + svg.appendChild json_to_svg filter: + id: 'crayon', filterUnits: 'userSpaceOnUse' + x: '-5%', y: '-5%', height: '110%', width: '110%' + children: [ + { feTurbulence: baseFrequency: '.3', numOctaves: '2', type: 'fractalNoise' } + { feDisplacementMap: scale: '6', xChannelSelector: 'R', in: 'SourceGraphic' } + ] + svg.appendChild json_to_svg style: + type: 'text/css' + contents: '.box.normal,.box.hover,.box.selected{filter: url(#crayon)}' + + # create canvas border + svg.appendChild json_to_svg rect: + x: 1 + y: supply_height + 1 + width: width - 2 + height: height - 2 - supply_height + class: 'canvas_border' + + supply = {} + for args, i in [ + { width: 40, height: 40 } + { width: 12, height: 50 } + { width: 70, height: 12 } + ] + widget = new RectWidget { + width: args.width + height: args.height + x: 30 + i * 90 + (70 - args.width) / 2 + y: (supply_height - args.height) / 2 + svg: svg + } + supply[widget.id] = widget + + # editor state + on_canvas = {} + selected = {} + editing = {} # has controls + dragging = false + drag_from = x: 0, y: 0 # mouse was here at last frame of drag + shift_key_down = false + + deselect_all = (args) -> + except = args?.except ? null + for id, w of selected + w.set_style STYLE_NORMAL + delete selected[id] + closest_widget = (widgets, xy) -> + prox = PROX_MAX + 1 + closest = null + for id, w of widgets + new_prox = w.proximity xy + if new_prox < prox + prox = new_prox + closest = w + if prox < PROX_MAX + return closest + return null + svg_event_to_xy = (e) -> + unless svg_offset? + svg_offset = $svg.offset() + return { + x: Math.round(e.pageX - svg_offset.left) + y: Math.round(e.pageY - svg_offset.top) + } + mousedown = (e) -> + mousemove e + if dragging # two mousedowns in a row?! it happens + return mouseup e + xy = svg_event_to_xy e + if xy.y < supply_height + closest = closest_widget supply, xy + if closest? + closest = closest.clone() + on_canvas[closest.id] = closest + else + closest = closest_widget on_canvas, xy + if closest? + unless (shift_key_down or selected[closest.id]?) + deselect_all except: closest + selected[closest.id] = closest + closest.set_style STYLE_DRAGGING + dragging = true + drag_from = xy + else + deselect_all() + return false + mouseup = (e) -> + mousemove e + if dragging + for id, w of selected + if w.y < supply_height + w.destruct() + delete selected[id] + else + w.set_style STYLE_SELECTED + dragging = false + return false + prev_hover = null + mousemove = (e) -> + xy = svg_event_to_xy e + if dragging + return if drag_from.x is xy.x and drag_from.y is xy.y + rel_x = xy.x - drag_from.x + rel_y = xy.y - drag_from.y + drag_from = xy + for id, w of selected + w.move x: w.x + rel_x, y: w.y + rel_y + else + hover = closest_widget on_canvas, xy + unless hover? + hover = closest_widget supply, xy + if hover != prev_hover + if prev_hover? + # FIXME + if selected[prev_hover.id]? + prev_hover.set_style STYLE_SELECTED + else + prev_hover.set_style STYLE_NORMAL + if hover? + hover.set_style STYLE_HOVER + prev_hover = hover + return false + $svg.mousedown mousedown + $svg.mouseup mouseup + $svg.mousemove mousemove + $(document).on 'keyup keydown', (e) -> + shift_key_down = e.shiftKey + return true + #($ document).keydown (e) -> + +$ init