Coffeescript dynamically create/call a function from a list on select box value change

99封情书 提交于 2020-01-06 19:41:32

问题


I'm working on adding some image editing tools using the Pixastic library. The idea is that the user can choose an aspect of the image or tool they want from a select box, then the tool will show up below the select box (I'm using select2) and the user can edit via a slider. Here's what I have so far:

# This seeds the select2 options list   
imageToolsList = [
    {id: 'bl', text: 'Blur'}
    {id: 'bc', text: 'Brightness/Contrast'}
    {id: 'ca', text: 'Color Adjust (RGB)'}
    ...
]

#Creates a select box and calls imageTooler function when the value of the box is changed
$(".image_tools_select").each ->
    $(@).select2
        placeholder: "Select an adjustment tool."
        data: imageToolsList
    $(@).on("change", (i) ->
        imageTooler JSON.stringify(
            val: i.val
            clipName: $(@).closest('.clip').attr('id')
        )
    )

# The function called on the value that the select box is changed to
imageTooler = (i) ->
    imageData = jQuery.parseJSON(i)
    iId = imageData.val
    imageClipName = imageData.clipName
    newTool = "<div id=#{iId}><label>#{iId}</label><div class='slider#{iId}'></div></div>"
    $("##{imageClipName}").find(".imagetoolfields").append newTool

This succeeds in appending the name of the editing tool and the correct slider div beneath the select box when a tool is chosen, but what I'd really like is dynamically create a slider function for that particular tool and image (there are multiple images on a page, each with their own editing toolbelt). Here's a slider function that works for a the 'Blur' tool:

$('.sliderbl').slider
    min: 0
    max: 5
    value: 0.5
    step: 0.1
    range: "min"
    slide: (event, ui) ->
        $("#img_snapshot_16").pixastic("blurfast", {amount:ui.value})

Is there a way to expand the imageToolsList so that it looks something like:

imageToolsList = [
    {id: 'bl', text: 'Blur', tool: $("##{imageClipName}").pixastic("blurfast", {amount:ui.value}), sliderVals: {min: 0, max: 5, value: 0.5, step: 0.1, range: "min"} }
    ...
]

and then dynamically create the jQuery slider functions for each tool in imageTooler, as is being done with the div and slider div?


回答1:


Comments get a little tedious for anything complicated so I'll just go ahead and map it all out. I've made a few assumptions about what is defined where and when but I don't think the assumptions matter that much.

We'll start with a simplified case: just one object similar to what you have in imageToolsList:

{
    id: 'bl'
    text: 'Blur'
    sliderVals: { min: 0, max: 5, value: 0.5, step: 0.1, range: "min" }
    tool: (imageClipName) ->
        (event, ui) -> $("##{imageClipName}").pixastic("blurfast", {amount:ui.value})
}

I've tweaked the order a little bit and switched tool to a function which returns a function. We don't want the pixastic call to happen while you're defining the object literals in imageToolsList, making tool a function allows us to defer the pixastic execution until later. Since we (presumably) don't know what imageClipName should be when we define imageToolsList, we need another function to allow us to fill that in with, again, calling pixastic until even later; hence the function returning a function trick.

Given one of these, how do we build a slider call? All we need to do is copy sliderVals (to avoid changing imageToolsList) and fill in the slide function:

sliderDef = { id: 'bl', ... }
doTheSliderThing = (imageClipName) ->
    slide = sliderDef.tool(imageClipName)
    args  = $.extend({ }, sliderDef.sliderVals, slide: slide)
    $(".slider#{sliderDef.id}").slider(args)

# And make it all go and pixastic-ify `#pancakes`.
doTheSliderThing('pancakes')

tool is a function which returns a callback function so sliderDef.tool(imageClipName) gives us the appropriate

(event, ui) -> $(...).pixastic(...)

callback function.

If we have an id and we want the appropriate entry from imageToolList, then we have to go looking for it:

# If the list is short:
[sliderDef] = (o for o in imageToolList when o.id == id)

The for loop gives you an array back and then the [sliderDef] unwraps that array and leaves the single result in sliderDef. If the imageToolList is longer then you'd want to short-circuit the loop and bail out as soon as you have a result:

# Longer list, bail out as soon as we've found what we're looking for.
for o in imageToolList when o.id == 2
    sliderDef = o
    break

or better, rework the structure of imageToolList to allow direct access by id:

# Even longer list: allow direct access by `id`.
imageToolList =
    bl: { text: 'Blur', sliderVals: { ... }, ... }
    ...

and then we can do things like this:

doTheSliderThing = (id, imageClipName) ->
    sliderDef = imageToolList[id]
    slide     = sliderDef.tool(imageClipName)
    args      = $.extend({ }, sliderDef.sliderVals, slide: slide)
    $(".slider#{id}").slider(args)

# And make it all go and pixastic-ify `#pancakes` using `'bl'`.
doTheSliderThing('bl', 'pancakes')

Or, if you prefer to be terse:

doTheSliderThing = (id, imageClipName) ->
    $(".slider#{id}").slider($.extend({ }
        imageToolList[id].sliderVals
        slide: imageToolList[id].tool(imageClipName)
    ))

Update for the comments: If you have this:

sliderDefs = 
    bl: { text: 'Blur', sliderVals: { ... }, ... }
    ...

Then you can build the stuff that slider2 wants like this:

opts = ({id: k, text: v.text} for k,v of sliderDefs)


来源:https://stackoverflow.com/questions/13198945/coffeescript-dynamically-create-call-a-function-from-a-list-on-select-box-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!