phetsims / joist

Joist is the main framework for PhET Interactive Simulations. Joist creates and displays the simulation content, home screen, navigation bar, About dialog, enables switching between tabs, and other framework-related features.
http://scenerystack.org/
MIT License
8 stars 6 forks source link

colors.html and Studio need a color editor that supports alpha #681

Closed pixelzoom closed 3 years ago

pixelzoom commented 3 years ago

colors.hmtl and Studio both use this color editor that is based on input type=color, and therefore cannot support alpha channel.

screenshot_775

This means that we currently cannot use alpha in colors that are specified in ColorProfile or in Properties editable by Studio.

So we need to come up with a new color editor that supports alpha, for use in colors.html and Studio.

In the meantime, I'm having to strip alpha channel out of colors in Fourier that we want to be editable via colors.html.

Slack discussion Chris Malley 7:46 PM I just discovered that {{repo}}-color.html does not support alpha channel. Does Studio's color editor support alpha, or does it use the same editor? New Sam Reid 7:47 PM checking… 7:49 Looks like RGB. It uses input.type=‘color’ Chris Malley 7:50 PM So if a designer wants to (for example) tweak a UI component that is by design translucent, I guess they are currently out of luck, eh? 7:51 Does this mean we need to avoid using the alpha channel for any colors that appear in ColorProfile or are editable via PhET-iO? Sam Reid 7:52 PM We could see if there is support for this in color inputs, or add our own transparency channel control? Chris Malley 7:53 PM Which repo do you think would be appropriate for an issue? Sam Reid 7:53 PM According to MDN, input type=color cannot support opacity: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/color Note: Setting the value to anything that isn’t a valid, fully-opaque, RGB color in hexadecimal notation will result in the value being set to #000000. 7:54 Is it for PhET-iO control or {repo}}-color.html or both? Chris Malley 7:54 PM Then a color editor that only supports input type=color is not a good fit for PhET. Sam Reid 7:54 PM Do we want to build a new color control that works in both PhET-iO and repo-color.html? Chris Malley 7:55 PM The case I ran into just now is for {repo}}-color.html. We had a Fourier design meeting today, and I was asked to add some things to ColorProfile, so that my design team could play with colors. Sam Reid 7:55 PM How about just putting transparency sliders next to the input type=color controls? Chris Malley 7:56 PM Sounds plausible. For now, I guess I'm stuck stripping out alpha channel where I can, and saying "sorry" where I can't. 7:57 For example, all of my chart grid lines are 'black', and I adjusted alpha to fade them out to the desired "look". I'm now having to convert those to rgb. Jonathan Olson 7:57 PM Presumably we can just not use the built in color editor browsers provide 7:58 It was just easier to use when I made the interface Chris Malley 7:58 PM Understood. Which repo for this change request? (edited) Jonathan Olson 7:58 PM I’ve used alphas in color profiles and that sounds nice

Assigning to @ariel-phet for assignment and prioritization.

samreid commented 3 years ago

Here's a quick feasibility test:

  window.addEventListener( 'message', function( evt ) {
    var data = JSON.parse( evt.data );
    if ( data.type === 'reportColor' ) {
      var name = data.name;
      var value = data.value;

      // Lazily create color inputs, so we don't need an up-front sim-specific list
      var input = colorInputMap[ name ];
      if ( !input ) {
        input = colorInputMap[ name ] = document.createElement( 'input' );
        input.type = 'color';

        // Send user changes to the sim
        input.addEventListener( 'input', function( evt ) {
          console.log( input.value );
          document.getElementById( 'sim-iframe' ).contentWindow.postMessage( JSON.stringify( {
            type: 'setColor',
            name: name,
            value: input.value,
            opacity: slider.valueAsNumber
          } ), '*' );
        } );

        document.body.appendChild( document.createElement( 'br' ) );
        document.body.appendChild( input );

        const slider = document.createElement( 'input' );
        slider.type = 'range';
        slider.min = 0;
        slider.max = 1;
        slider.step = 0.01;
        slider.value = input.value.opacity;
        slider.addEventListener( 'input', evt => {
          document.getElementById( 'sim-iframe' ).contentWindow.postMessage( JSON.stringify( {
            type: 'setColor',
            name: name,
            value: input.value,
            opacity: slider.valueAsNumber
          } ), '*' );
        } );
        document.body.appendChild( slider );
        document.body.appendChild( document.createTextNode( ' ' + name ) );
      }

      input.value = value;
    }
  } )

And in ColorProfile.js

      if ( data && data.type === 'setColor' ) {
        const c = new Color( data.value );
        this[ data.name + 'Property' ].value = new Color( c.red,c.green,c.blue,data.opacity );
      }

image

samreid commented 3 years ago

Maybe we should use https://github.com/Simonwep/pickr

Flat, simple, multi-themed, responsive and hackable Color-Picker library. No dependencies, no jQuery. Compatible with all CSS Frameworks e.g. Bootstrap, Materialize. Supports alpha channel, rgba, hsla, hsva and more!

MIT License. Looks like this:

image

pixelzoom commented 3 years ago

Re https://github.com/phetsims/joist/issues/681#issuecomment-747839283... Thanks for cranking out a feasibility test so quickly. Having to edit RGB in one place, and A in a different place (with the RGB picker closed) is not ideal, but better than no alpha support.

Re https://github.com/phetsims/joist/issues/681#issuecomment-747840788... Looks nice to me. Demo at https://simonwep.github.io/pickr/

ariel-phet commented 3 years ago

@samreid this seems like a topic to bring up at iO meeting. It would be good to get a read on @kathy-phet feeling of priority

zepumph commented 3 years ago

Related to https://github.com/phetsims/scenery/issues/1135.

samreid commented 3 years ago

We discussed in https://github.com/phetsims/scenery/issues/1135#issuecomment-756431512 and the following comment.

zepumph commented 3 years ago

I'm really excited by https://www.npmjs.com/package/vanilla-picker, can we just use that in those two spots? I forget, can we put MIT stuff in PhET-iO?

samreid commented 3 years ago

PhET-iO can use MIT code. Can you contrast https://www.npmjs.com/package/vanilla-picker and https://github.com/Simonwep/pickr?

zepumph commented 3 years ago

pickr seems much better. Let's do that.

jonathanolson commented 3 years ago

This came up when I was instrumenting color Properties in Density/Buoyancy, editing the water color instantly turns it opaque.

zepumph commented 3 years ago

@jonathanolson is there a timeline on Density/Buoyancy phet-io that we should know about for the priority of this issue?

kathy-phet commented 3 years ago

Our client sent email yesterday that this sim is actually highest on their phet-io list. We can discuss tomorrow exact timing/feasibility for this goal tomorrow.

zepumph commented 3 years ago

Sounds good to me!

zepumph commented 3 years ago

Here is a pretty easy hello world I was using with a CDN. I think we should use the "classic" theme, because it is clean and easy.

```html




































```
zepumph commented 3 years ago

I think we don't want the "save" option, we should just update as you mutate the picker.

zepumph commented 3 years ago

I'll add to sherpa and start in the colors.html file.

zepumph commented 3 years ago

From investigation, I found that it caused a few problems to use pickr. Mostly it was impossible to get the pickr to be a span, but had to be a div. This takes up more vertical space than is desired, and also meant that I had to make sure they were lower in the z-index than the sim. I created https://github.com/Simonwep/pickr/issues/289 to investigate, but for now I think we can commit and continue with the work arounds in place.

zepumph commented 3 years ago

I may have to keep looking through options like https://github.com/Simonwep/pickr#options, but in general I have the following things still to figure out:

I'm not actually sure that I'm all in on pickr right now. It is potentially workable, but it has been far from easy to sort out its quirks. Here is my current progress, I can't really commit to it right now.

```diff Index: sherpa/lib/pickr-1.8.1.es5.min.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/sherpa/lib/pickr-1.8.1.es5.min.js b/sherpa/lib/pickr-1.8.1.es5.min.js new file mode 100644 --- /dev/null (date 1626971512871) +++ b/sherpa/lib/pickr-1.8.1.es5.min.js (date 1626971512871) @@ -0,0 +1,4 @@ +/*! Pickr 1.8.1 MIT | https://github.com/Simonwep/pickr */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Pickr=e():t.Pickr=e()}(window,(function(){return function(t){var e={};function r(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)r.d(n,o,function(e){return t[e]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=140)}([function(t,e,r){var n=r(3),o=r(14).f,i=r(13),a=r(16),c=r(44),u=r(77),s=r(82);t.exports=function(t,e){var r,l,f,p,v,h=t.target,d=t.global,g=t.stat;if(r=d?n:g?n[h]||c(h,{}):(n[h]||{}).prototype)for(l in e){if(p=e[l],f=t.noTargetGet?(v=o(r,l))&&v.value:r[l],!s(d?l:h+(g?".":"#")+l,t.forced)&&void 0!==f){if(typeof p==typeof f)continue;u(p,f)}(t.sham||f&&f.sham)&&i(p,"sham",!0),a(r,l,p,t)}}},function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e,r){var n=r(3),o=r(31),i=r(5),a=r(46),c=r(51),u=r(84),s=o("wks"),l=n.Symbol,f=u?l:l&&l.withoutSetter||a;t.exports=function(t){return i(s,t)&&(c||"string"==typeof s[t])||(c&&i(l,t)?s[t]=l[t]:s[t]=f("Symbol."+t)),s[t]}},function(t,e,r){(function(e){var r=function(t){return t&&t.Math==Math&&t};t.exports=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof e&&e)||function(){return this}()||Function("return this")()}).call(this,r(108))},function(t,e,r){var n=r(7);t.exports=function(t){if(!n(t))throw TypeError(String(t)+" is not an object");return t}},function(t,e,r){var n=r(12),o={}.hasOwnProperty;t.exports=function(t,e){return o.call(n(t),e)}},function(t,e,r){var n=r(1);t.exports=!n((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}))},function(t,e){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,e,r){var n=r(6),o=r(74),i=r(4),a=r(19),c=Object.defineProperty;e.f=n?c:function(t,e,r){if(i(t),e=a(e,!0),i(r),o)try{return c(t,e,r)}catch(t){}if("get"in r||"set"in r)throw TypeError("Accessors not supported");return"value"in r&&(t[e]=r.value),t}},function(t,e,r){var n=r(17),o=Math.min;t.exports=function(t){return t>0?o(n(t),9007199254740991):0}},function(t,e,r){var n=r(28),o=r(11);t.exports=function(t){return n(o(t))}},function(t,e){t.exports=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t}},function(t,e,r){var n=r(11);t.exports=function(t){return Object(n(t))}},function(t,e,r){var n=r(6),o=r(8),i=r(18);t.exports=n?function(t,e,r){return o.f(t,e,i(1,r))}:function(t,e,r){return t[e]=r,t}},function(t,e,r){var n=r(6),o=r(43),i=r(18),a=r(10),c=r(19),u=r(5),s=r(74),l=Object.getOwnPropertyDescriptor;e.f=n?l:function(t,e){if(t=a(t),e=c(e,!0),s)try{return l(t,e)}catch(t){}if(u(t,e))return i(!o.f.call(t,e),t[e])}},function(t,e){var r={}.toString;t.exports=function(t){return r.call(t).slice(8,-1)}},function(t,e,r){var n=r(3),o=r(13),i=r(5),a=r(44),c=r(76),u=r(29),s=u.get,l=u.enforce,f=String(String).split("String");(t.exports=function(t,e,r,c){var u,s=!!c&&!!c.unsafe,p=!!c&&!!c.enumerable,v=!!c&&!!c.noTargetGet;"function"==typeof r&&("string"!=typeof e||i(r,"name")||o(r,"name",e),(u=l(r)).source||(u.source=f.join("string"==typeof e?e:""))),t!==n?(s?!v&&t[e]&&(p=!0):delete t[e],p?t[e]=r:o(t,e,r)):p?t[e]=r:a(e,r)})(Function.prototype,"toString",(function(){return"function"==typeof this&&s(this).source||c(this)}))},function(t,e){var r=Math.ceil,n=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?n:r)(t)}},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e,r){var n=r(7);t.exports=function(t,e){if(!n(t))return t;var r,o;if(e&&"function"==typeof(r=t.toString)&&!n(o=r.call(t)))return o;if("function"==typeof(r=t.valueOf)&&!n(o=r.call(t)))return o;if(!e&&"function"==typeof(r=t.toString)&&!n(o=r.call(t)))return o;throw TypeError("Can't convert object to primitive value")}},function(t,e){t.exports=!1},function(t,e,r){var n=r(86),o=r(28),i=r(12),a=r(9),c=r(53),u=[].push,s=function(t){var e=1==t,r=2==t,s=3==t,l=4==t,f=6==t,p=7==t,v=5==t||f;return function(h,d,g,y){for(var b,m,w=i(h),x=o(w),S=n(d,g,3),_=a(x.length),O=0,A=y||c,E=e?A(h,_):r||p?A(h,0):void 0;_>O;O++)if((v||O in x)&&(m=S(b=x[O],O,w),t))if(e)E[O]=m;else if(m)switch(t){case 3:return!0;case 5:return b;case 6:return O;case 2:u.call(E,b)}else switch(t){case 4:return!1;case 7:u.call(E,b)}return f?-1:s||l?l:E}};t.exports={forEach:s(0),map:s(1),filter:s(2),some:s(3),every:s(4),find:s(5),findIndex:s(6),filterOut:s(7)}},function(t,e,r){"use strict";var n=r(0),o=r(39);n({target:"RegExp",proto:!0,forced:/./.exec!==o},{exec:o})},function(t,e,r){var n=r(58),o=r(16),i=r(116);n||o(Object.prototype,"toString",i,{unsafe:!0})},function(t,e,r){"use strict";var n=r(19),o=r(8),i=r(18);t.exports=function(t,e,r){var a=n(e);a in t?o.f(t,a,i(0,r)):t[a]=r}},function(t,e,r){var n=r(1),o=r(2),i=r(52),a=o("species");t.exports=function(t){return i>=51||!n((function(){var e=[];return(e.constructor={})[a]=function(){return{foo:1}},1!==e[t](Boolean).foo}))}},function(t,e){t.exports={}},function(t,e,r){var n=r(0),o=r(110);n({target:"Object",stat:!0,forced:Object.assign!==o},{assign:o})},function(t,e,r){var n=r(1),o=r(15),i="".split;t.exports=n((function(){return!Object("z").propertyIsEnumerable(0)}))?function(t){return"String"==o(t)?i.call(t,""):Object(t)}:Object},function(t,e,r){var n,o,i,a=r(109),c=r(3),u=r(7),s=r(13),l=r(5),f=r(45),p=r(30),v=r(32),h=c.WeakMap;if(a||f.state){var d=f.state||(f.state=new h),g=d.get,y=d.has,b=d.set;n=function(t,e){if(y.call(d,t))throw new TypeError("Object already initialized");return e.facade=t,b.call(d,t,e),e},o=function(t){return g.call(d,t)||{}},i=function(t){return y.call(d,t)}}else{var m=p("state");v[m]=!0,n=function(t,e){if(l(t,m))throw new TypeError("Object already initialized");return e.facade=t,s(t,m,e),e},o=function(t){return l(t,m)?t[m]:{}},i=function(t){return l(t,m)}}t.exports={set:n,get:o,has:i,enforce:function(t){return i(t)?o(t):n(t,{})},getterFor:function(t){return function(e){var r;if(!u(e)||(r=o(e)).type!==t)throw TypeError("Incompatible receiver, "+t+" required");return r}}}},function(t,e,r){var n=r(31),o=r(46),i=n("keys");t.exports=function(t){return i[t]||(i[t]=o(t))}},function(t,e,r){var n=r(20),o=r(45);(t.exports=function(t,e){return o[t]||(o[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.12.1",mode:n?"pure":"global",copyright:"© 2021 Denis Pushkarev (zloirock.ru)"})},function(t,e){t.exports={}},function(t,e,r){var n=r(79),o=r(3),i=function(t){return"function"==typeof t?t:void 0};t.exports=function(t,e){return arguments.length<2?i(n[t])||i(o[t]):n[t]&&n[t][e]||o[t]&&o[t][e]}},function(t,e,r){var n=r(80),o=r(48).concat("length","prototype");e.f=Object.getOwnPropertyNames||function(t){return n(t,o)}},function(t,e,r){var n=r(80),o=r(48);t.exports=Object.keys||function(t){return n(t,o)}},function(t,e,r){var n,o=r(4),i=r(112),a=r(48),c=r(32),u=r(113),s=r(75),l=r(30),f=l("IE_PROTO"),p=function(){},v=function(t){return" + + + - + + + Index: sherpa/lib/pickr-1.8.1.classic.min.css IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/sherpa/lib/pickr-1.8.1.classic.min.css b/sherpa/lib/pickr-1.8.1.classic.min.css new file mode 100644 --- /dev/null (date 1626971546511) +++ b/sherpa/lib/pickr-1.8.1.classic.min.css (date 1626971546511) @@ -0,0 +1,1 @@ +/*! Pickr 1.8.1 MIT | https://github.com/Simonwep/pickr */.pickr{position:relative;overflow:visible;transform:translateY(0)}.pickr *{box-sizing:border-box;outline:none;border:none;-webkit-appearance:none}.pickr .pcr-button{position:relative;height:2em;width:2em;padding:.5em;cursor:pointer;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;border-radius:.15em;background:url('data:image/svg+xml;utf8, ') no-repeat 50%;background-size:0;transition:all .3s}.pickr .pcr-button:before{background:url('data:image/svg+xml;utf8, ');background-size:.5em;z-index:-1;z-index:auto}.pickr .pcr-button:after,.pickr .pcr-button:before{position:absolute;content:"";top:0;left:0;width:100%;height:100%;border-radius:.15em}.pickr .pcr-button:after{transition:background .3s;background:var(--pcr-color)}.pickr .pcr-button.clear{background-size:70%}.pickr .pcr-button.clear:before{opacity:0}.pickr .pcr-button.clear:focus{box-shadow:0 0 0 1px hsla(0,0%,100%,.85),0 0 0 3px var(--pcr-color)}.pickr .pcr-button.disabled{cursor:not-allowed}.pcr-app *,.pickr *{box-sizing:border-box;outline:none;border:none;-webkit-appearance:none}.pcr-app button.pcr-active,.pcr-app button:focus,.pcr-app input.pcr-active,.pcr-app input:focus,.pickr button.pcr-active,.pickr button:focus,.pickr input.pcr-active,.pickr input:focus{box-shadow:0 0 0 1px hsla(0,0%,100%,.85),0 0 0 3px var(--pcr-color)}.pcr-app .pcr-palette,.pcr-app .pcr-slider,.pickr .pcr-palette,.pickr .pcr-slider{transition:box-shadow .3s}.pcr-app .pcr-palette:focus,.pcr-app .pcr-slider:focus,.pickr .pcr-palette:focus,.pickr .pcr-slider:focus{box-shadow:0 0 0 1px hsla(0,0%,100%,.85),0 0 0 3px rgba(0,0,0,.25)}.pcr-app{position:fixed;display:flex;flex-direction:column;z-index:10000;border-radius:.1em;background:#fff;opacity:0;visibility:hidden;transition:opacity .3s,visibility 0s .3s;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;box-shadow:0 .15em 1.5em 0 rgba(0,0,0,.1),0 0 1em 0 rgba(0,0,0,.03);left:0;top:0}.pcr-app.visible{transition:opacity .3s;visibility:visible;opacity:1}.pcr-app .pcr-swatches{display:flex;flex-wrap:wrap;margin-top:.75em}.pcr-app .pcr-swatches.pcr-last{margin:0}@supports (display:grid){.pcr-app .pcr-swatches{display:grid;align-items:center;grid-template-columns:repeat(auto-fit,1.75em)}}.pcr-app .pcr-swatches>button{font-size:1em;position:relative;width:calc(1.75em - 5px);height:calc(1.75em - 5px);border-radius:.15em;cursor:pointer;margin:2.5px;flex-shrink:0;justify-self:center;transition:all .15s;overflow:hidden;background:transparent;z-index:1}.pcr-app .pcr-swatches>button:before{position:absolute;content:"";top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, ');background-size:6px;border-radius:.15em;z-index:-1}.pcr-app .pcr-swatches>button:after{content:"";position:absolute;top:0;left:0;width:100%;height:100%;background:var(--pcr-color);border:1px solid rgba(0,0,0,.05);border-radius:.15em;box-sizing:border-box}.pcr-app .pcr-swatches>button:hover{filter:brightness(1.05)}.pcr-app .pcr-swatches>button:not(.pcr-active){box-shadow:none}.pcr-app .pcr-interaction{display:flex;flex-wrap:wrap;align-items:center;margin:0 -.2em}.pcr-app .pcr-interaction>*{margin:0 .2em}.pcr-app .pcr-interaction input{letter-spacing:.07em;font-size:.75em;text-align:center;cursor:pointer;color:#75797e;background:#f1f3f4;border-radius:.15em;transition:all .15s;padding:.45em .5em;margin-top:.75em}.pcr-app .pcr-interaction input:hover{filter:brightness(.975)}.pcr-app .pcr-interaction input:focus{box-shadow:0 0 0 1px hsla(0,0%,100%,.85),0 0 0 3px rgba(66,133,244,.75)}.pcr-app .pcr-interaction .pcr-result{color:#75797e;text-align:left;flex:1 1 8em;min-width:8em;transition:all .2s;border-radius:.15em;background:#f1f3f4;cursor:text}.pcr-app .pcr-interaction .pcr-result::-moz-selection{background:#4285f4;color:#fff}.pcr-app .pcr-interaction .pcr-result::selection{background:#4285f4;color:#fff}.pcr-app .pcr-interaction .pcr-type.active{color:#fff;background:#4285f4}.pcr-app .pcr-interaction .pcr-cancel,.pcr-app .pcr-interaction .pcr-clear,.pcr-app .pcr-interaction .pcr-save{width:auto;color:#fff}.pcr-app .pcr-interaction .pcr-cancel:hover,.pcr-app .pcr-interaction .pcr-clear:hover,.pcr-app .pcr-interaction .pcr-save:hover{filter:brightness(.925)}.pcr-app .pcr-interaction .pcr-save{background:#4285f4}.pcr-app .pcr-interaction .pcr-cancel,.pcr-app .pcr-interaction .pcr-clear{background:#f44250}.pcr-app .pcr-interaction .pcr-cancel:focus,.pcr-app .pcr-interaction .pcr-clear:focus{box-shadow:0 0 0 1px hsla(0,0%,100%,.85),0 0 0 3px rgba(244,66,80,.75)}.pcr-app .pcr-selection .pcr-picker{position:absolute;height:18px;width:18px;border:2px solid #fff;border-radius:100%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.pcr-app .pcr-selection .pcr-color-chooser,.pcr-app .pcr-selection .pcr-color-opacity,.pcr-app .pcr-selection .pcr-color-palette{position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:flex;flex-direction:column;cursor:grab;cursor:-webkit-grab}.pcr-app .pcr-selection .pcr-color-chooser:active,.pcr-app .pcr-selection .pcr-color-opacity:active,.pcr-app .pcr-selection .pcr-color-palette:active{cursor:grabbing;cursor:-webkit-grabbing}.pcr-app[data-theme=classic]{width:28.5em;max-width:95vw;padding:.8em}.pcr-app[data-theme=classic] .pcr-selection{display:flex;justify-content:space-between;flex-grow:1}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-preview{position:relative;z-index:1;width:2em;display:flex;flex-direction:column;justify-content:space-between;margin-right:.75em}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-preview:before{position:absolute;content:"";top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, ');background-size:.5em;border-radius:.15em;z-index:-1}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-preview .pcr-last-color{cursor:pointer;transition:background-color .3s,box-shadow .3s;border-radius:.15em .15em 0 0;z-index:2}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-preview .pcr-current-color{border-radius:0 0 .15em .15em}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-preview .pcr-current-color,.pcr-app[data-theme=classic] .pcr-selection .pcr-color-preview .pcr-last-color{background:var(--pcr-color);width:100%;height:50%}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-palette{width:100%;height:8em;z-index:1}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-palette .pcr-palette{flex-grow:1;border-radius:.15em}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-palette .pcr-palette:before{position:absolute;content:"";top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, ');background-size:.5em;border-radius:.15em;z-index:-1}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-chooser,.pcr-app[data-theme=classic] .pcr-selection .pcr-color-opacity{margin-left:.75em}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-chooser .pcr-picker,.pcr-app[data-theme=classic] .pcr-selection .pcr-color-opacity .pcr-picker{left:50%;transform:translateX(-50%)}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-chooser .pcr-slider,.pcr-app[data-theme=classic] .pcr-selection .pcr-color-opacity .pcr-slider{width:8px;flex-grow:1;border-radius:50em}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-chooser .pcr-slider{background:linear-gradient(180deg,red,#ff0,#0f0,#0ff,#00f,#f0f,red)}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-opacity .pcr-slider{background:linear-gradient(180deg,transparent,#000),url('data:image/svg+xml;utf8, ');background-size:100%,50%} \ No newline at end of file ```
samreid commented 3 years ago

That patch seems nice so far, may be still workable. I didn't understand why the z-indices were important. I removed them and wasn't sure what the problem was. Also, maybe the main page should use css-grid for layout. I also noticed https://github.com/web-padawan/vanilla-colorful which also seemed OK.

zepumph commented 3 years ago

The divs for each button extend onto and over the sim. try to drag vertically something in a sim and feel it get interrupted as you move over the divs.

I'd be happier most likely trying out another package. A small aside is that it would be nice to have this be a single file, instead of a js file and a css file that need to be imported where ever this is used.

Furthermore. I wanted to note that even with basic implementation, it seemed like most of our colors weren't responding to the alpha, and the density example of the water wasn't actually behaving as desired. I'm not sure if it is because of scenery, or because individual color usages may not be set up to support color opacity.

samreid commented 3 years ago

I'm planning on working on this in the near future.

samreid commented 3 years ago

https://github.com/Simonwep/pickr says:

This project will be archived at the end of 2021! After this there will be no more bug / security fixes or feature requests.

Considering that and the list of constraints mentioned in https://github.com/phetsims/joist/issues/681#issuecomment-885259345 is a good reason to avoid it. Likewise, https://github.com/web-padawan/vanilla-colorful seems nice, but I'm sure it will have its quirks and constraints, and it does not have a critical mass to ensure it will be well-maintained in the long term. I considered creating our own color picker (we have done variants of this via the spectrum sliders in scenery), but it seems wasteful to recreate so much of the built-in input type=color support, and then we would introduce our own maintenance burden. Is there a way to streamline the "additional transparency slider" like in https://github.com/phetsims/joist/issues/681#issuecomment-747839283 user experience or will that always be clunky?

As a first step, I'd like to make the Color Editor more responsive. It has the following problems:

  1. The sim size doesn't respect the size of the elements and can overlap them if they are long or if the window is thin.
  2. The sim goes off the window if the window is too thin
  3. If you scroll to see the colors at the bottom of the list, the sim is out of view.
samreid commented 3 years ago

I added the transparency sliders. They look like a designer's nightmare, but on the other hand they have the following advantages:

Currently, the alpha only outputs to the console (it is not shown in the UI).

I'll consider what this may look like for studio.

samreid commented 3 years ago

I added similar support to studio, it looks like this and is wired up and working well. There is already an alpha channel numerical readout.

image

samreid commented 3 years ago

@pixelzoom said:

In the meantime, I'm having to strip alpha channel out of colors in Fourier that we want to be editable via colors.html.

Based on the above changes, I think you no longer need to strip alpha channels out of colors in Fourier.

samreid commented 3 years ago

@arouinfar and/or @amanda-phet for the sim color editor, would you like the alpha value displayed in the page, or is it OK for it to be in the console? UPDATE: I added a readout in the color editor.

@zepumph the implementation is ready for review.

pixelzoom commented 3 years ago

While I appreciate the effort that has gone into trying to locate a color editor that supports alpha, then implementing this workaround... I really dislike this UI, having to edit alpha separately, and possibly having to look in the console to see what value the slider is setting. Very difficult to set a specific alpha value.

Are you planning for this to be a shortterm workaround, or a final solution?

samreid commented 3 years ago

possibly having to look in the console to see what value the slider is setting. Very difficult to set a specific alpha value.

After the commit, the transparency value is displayed in the Color Editor.

I really dislike this UI, having to edit alpha separately

I agree it is not a streamlined user experience. Let's discuss further along these dimensions:

The color editor

Is the color editor good enough for our own internal team use? For some of our internal team utilities, we do not maintain the same quality of design standard as we do for our end-user products (I'm not arguing that this is a good thing). Also, from a brief survey of several simulations, it seems transparency is not widely used (nearly all alpha values are 1.0). Is that just because it hasn't been supported until now? Or are most editable colors in a sim opaque? If it is just for our team and not widely used, maybe it doesn't warrant a more expensive solution? How much more effort is it worth?

For Studio

How do we value using the native input type=color vs something nonstandard? Would it be worse to provide 4 sliders, one for each of RGBA, without a visual indicator? Then at least they would be uniform.

For the code review:

Are you planning for this to be a shortterm workaround, or a final solution?

I was hopeful this direction would be "good enough" (maybe with some refinement and iteration), but I'm happy to keep improving it if we agree it is important to integrate the transparency control with the RGB controls. I'm somewhat interested in exploring a canvas-based picker, knowing that a11y will be a lot of extra work for it. I'd also be happy to take https://github.com/web-padawan/vanilla-colorful for a test drive.

Also, I noticed https://web-padawan.github.io/vanilla-colorful/ looks nice and supports a11y, but doesn't have a "type it in" interface. Is that a feature we need to have integrated with this control?

UPDATE: Based on the last remark, I'm not confident in exploring 3rd party implementations until we have a design specification. If we adapt to use a 3rd party solution only to find it doesn't support "type it in" interface (or other features), and we decide we need that, then there is not a good way forward. We have a bit of slack if we build our own, but again, that design should be specified before we start.

UPDATE 2: Until we have a design specification, let's continue with the "transparency slider" strategy, since it seems better than nothing.

samreid commented 3 years ago

In the commit, the transparency slider updates when the sim color transparency changes. I tested with this patch in proportionPlaygroundColorProfile:

setInterval(()=>{
  proportionPlaygroundColorProfile.exploreBackgroundProperty.value =proportionPlaygroundColorProfile.exploreBackgroundProperty.value.withAlpha(Math.random());
},1000);

I think a good next step would be to touch base at a phet-io or design meeting.

samreid commented 3 years ago

We discussed this at today's meeting.

@arouinfar said it is important to be able to type in HEX or RGB color values. @kathy-phet said it is OK in studio with the separate transparency slider.

We do not plan for this to be used a lot by clients. Most opacities will just be 1.

We haven't exposed colors to PhET-iO before, so this is low priority and not necessary to refine further. We don't have a client need for this at the moment, and don't want to invest further in this at the moment.

Query parameters may also be used to specify some colors, as necessary.

@pixelzoom points out that we need to be forward thinking about high contrast mode support.

Back to @samreid to see if this issue can be closed.

zepumph commented 3 years ago

Just noting here, that I am really really happy with this UI choice, and glad that designers agreed. It is simple, and easy to implement. As much native HTML that can be used it good for me! Perhaps one day the input type=color will support alpha, and we could get rid of our sliders (likely not though).

samreid commented 3 years ago

It looks like this implementation has not been code reviewed. @zepumph can you please take a look?

zepumph commented 3 years ago

Review:

https://github.com/phetsims/scenery/commit/d20257d4c968859ce32745d51dc78160ea5f178c is very excellent. Much nicer than what I was doing over in https://github.com/phetsims/scenery/issues/1253. Thanks!

// DUPLICATION ALERT: Copied with code in PhetioElementView.js

Yes this is duplication, but I'm not worried about it, it is all just wiring up a basic slider to adapt to a value. It doesn't bother me personally.

No suggestions. Thanks @samreid!