【问题标题】:Javascript canvas color not smooth change in brightnessJavascript画布颜色亮度变化不平滑
【发布时间】:2018-04-10 06:54:34
【问题描述】:

我正在谈论的完整演示在这里: https://ggodfrey.github.io/ComplexColor/index.html

我正在尝试制作一个可以尝试使用颜色绘制复值函数的网站。色调是使用复数答案的角度确定的,亮度是通过对复数答案的大小取对数然后找到小数部分来确定的。从那里,我使用一个函数将 HSL 转换为 RGB,然后将其放入我在画布上绘制的 Image 对象中,从而允许我在每个像素上进行绘制。

如上页所示,亮度“级别”在对数从一个整数变为另一个整数之间具有“粗糙”边缘。它应该看起来更像this。这个问题与我实际计算亮度的方式或使用 javascript 画布有关吗?

  window.onload = function(){

    var EQUATION = '';
    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext('2d');
    var x_min = -3;
    var x_max = 3;
    var y_min = -3;
    var y_max = 3;
    var image = ctx.createImageData(canvas.width, canvas.height);

    Complex = function(re, im){
      this.re = re;
      this.im = im;
    }

    Complex.prototype.add = function(other){
      return new Complex(this.re+other.re, this.im+other.im);
    }

    Complex.prototype.multiply = function(other){
      return new Complex(this.re*other.re-other.im*this.im, this.re*other.im+this.im*other.re);
    }

    Complex.prototype.power = function(num){
      var r = this.magnitude();
      var theta = this.angle();
      var a = Math.pow(r, num)*Math.cos(num*theta);
      var b = Math.pow(r, num)*Math.sin(num*theta);
      return new Complex(a, b);
    }

    Complex.prototype.magnitude = function(){
      return Math.pow(Math.pow(this.re, 2) + Math.pow(this.im, 2), 0.5);
    }

    Complex.prototype.angle = function(){
      return Math.atan2(this.im, this.re);
    }

    Complex.prototype.divide = function(other){
      x = new Complex(this.re, this.im);
      y = new Complex(other.re, other.im);
      x = x.multiply(new Complex(other.re, -other.im));
      y = y.multiply(new Complex(other.re, -other.im));
      x = new Complex(x.re/y.re, x.im/y.re);
      return x;
    }

    function hslToRgb(h, s, l){ //NOT MY CODE
      var r, g, b;

      if(s == 0){
        r = g = b = l; // achromatic
      } else {
        function hue2rgb(p, q, t){
          if(t < 0) t += 1;
          if(t > 1) t -= 1;
          if(t < 1/6) return p + (q - p) * 6 * t;
          if(t < 1/2) return q;
          if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
          return p;
        }
        var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
        var p = 2 * l - q;
        r = hue2rgb(p, q, h + 1/3.0);
        g = hue2rgb(p, q, h);
        b = hue2rgb(p, q, h - 1/3.0);
      }
      return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
    }

    function evaluate(i, j){
      var z = new Complex(x_min+j*(x_max-x_min)/canvas.width, y_min+i*(y_max-y_min)/canvas.height);
      var num = z.power(2).add(new Complex(-1, 0)).multiply(z.add(new Complex(-2, -1)).power(2));
      var den = z.power(2).add(new Complex(2, 4));
      var end = num.divide(den);
      var color = end.angle()/(2*Math.PI);
      var brightness = end.magnitude();
      brightness = Math.log(brightness)/Math.log(2) % 1;
      return [color, brightness];
    }

    function main(){
      var data = image.data;
      if(EQUATION !== null){
        var count = 0;
        for(var i=0;i<canvas.height;i++){
          for(var j=0;j<canvas.width;j++){

            var c = evaluate(i, j);
            rgb = hslToRgb(c[0], 1, 0.4+c[1]/5);
            var r = rgb[0];
            var g = rgb[1];
            var b = rgb[2];
            var c = count*4;
            data[c] = r;
            data[c+1] = g;
            data[c+2] = b;
            data[c+3] = 255;
            count++;
          }
        }
        image.data = data;
        ctx.putImageData(image, 0, 0);
    }
  }

  main();

  function getMousePos(canvas, evt){
    var rect = canvas.getBoundingClientRect();
    return {x: (evt.clientX-rect.left)/(rect.right-rect.left)*canvas.width,
      y: (evt.clientY-rect.top)/(rect.bottom-rect.top)*canvas.height};
  }

  document.getElementById("submit").addEventListener("mousedown", function(event){
    EQUATION = document.getElementById("equation").innerHTML;
    var x = main();
  })

  document.getElementById("canvas").addEventListener("mousemove", function(event){
    var loc = getMousePos(canvas, event);
    document.getElementById('x').innerHTML = Math.round(loc.x*100)/100;
    document.getElementById('y').innerHTML = Math.round(loc.y*100)/100;
    document.getElementById('brightness').innerHTML = evaluate(loc.y, loc.x)[1];
  })

}
<head>
  <title>Complex Color</title>
  <meta charset="utf-8"></head>
<body>
  <input id="equation" type="text">Type Equation</input><button id="submit">Submit</button><br>
  <canvas id="canvas" style="width:500px;height:500px"></canvas><p> <span id="x"></span>, <span id="y"></span>, <span id="brightness"></span></p>
</body>

【问题讨论】:

    标签: javascript html5-canvas complex-numbers


    【解决方案1】:

    假设公式正确:

    • 增加画布的bitmap resolution 并使用更小的 CSS 尺寸来引入平滑 - 或 - 实现手动抗锯齿。这是因为您逐个像素地写入,绕过了抗锯齿。

    • 将饱和度降低到 80% 左右:rgb = hslToRgb(c[0], 0.8, 0.4 + c[1] / 5);。 100% 通常会在屏幕上产生看起来过饱和的图像。打印虽然使用 100%。

    var EQUATION = '';
    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext('2d');
    var x_min = -3;
    var x_max = 3;
    var y_min = -3;
    var y_max = 3;
    var image = ctx.createImageData(canvas.width, canvas.height);
    
    Complex = function(re, im) {
      this.re = re;
      this.im = im;
    }
    
    Complex.prototype.add = function(other) {
      return new Complex(this.re + other.re, this.im + other.im);
    }
    
    Complex.prototype.multiply = function(other) {
      return new Complex(this.re * other.re - other.im * this.im, this.re * other.im + this.im * other.re);
    }
    
    Complex.prototype.power = function(num) {
      var r = this.magnitude();
      var theta = this.angle();
      var a = Math.pow(r, num) * Math.cos(num * theta);
      var b = Math.pow(r, num) * Math.sin(num * theta);
      return new Complex(a, b);
    }
    
    Complex.prototype.magnitude = function() {
      return Math.pow(Math.pow(this.re, 2) + Math.pow(this.im, 2), 0.5);
    }
    
    Complex.prototype.angle = function() {
      return Math.atan2(this.im, this.re);
    }
    
    Complex.prototype.divide = function(other) {
      x = new Complex(this.re, this.im);
      y = new Complex(other.re, other.im);
      x = x.multiply(new Complex(other.re, -other.im));
      y = y.multiply(new Complex(other.re, -other.im));
      x = new Complex(x.re / y.re, x.im / y.re);
      return x;
    }
    
    function hslToRgb(h, s, l) { //NOT MY CODE
      var r, g, b;
    
      if (s == 0) {
        r = g = b = l; // achromatic
      } else {
        function hue2rgb(p, q, t) {
          if (t < 0) t += 1;
          if (t > 1) t -= 1;
          if (t < 1 / 6) return p + (q - p) * 6 * t;
          if (t < 1 / 2) return q;
          if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
          return p;
        }
        var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
        var p = 2 * l - q;
        r = hue2rgb(p, q, h + 1 / 3.0);
        g = hue2rgb(p, q, h);
        b = hue2rgb(p, q, h - 1 / 3.0);
      }
      return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
    }
    
    function evaluate(i, j) {
      var z = new Complex(x_min + j * (x_max - x_min) / canvas.width, y_min + i * (y_max - y_min) / canvas.height);
      var num = z.power(2).add(new Complex(-1, 0)).multiply(z.add(new Complex(-2, -1)).power(2));
      var den = z.power(2).add(new Complex(2, 4));
      var end = num.divide(den);
      var color = end.angle() / (2 * Math.PI);
      var brightness = end.magnitude();
      brightness = Math.log(brightness) / Math.log(2) % 1;
      return [color, brightness];
    }
    
    function main() {
      var data = image.data;
      if (EQUATION !== null) {
        var count = 0;
        for (var i = 0; i < canvas.height; i++) {
          for (var j = 0; j < canvas.width; j++) {
    
            var c = evaluate(i, j);
            rgb = hslToRgb(c[0], 0.8, 0.4 + c[1] / 5);
            var r = rgb[0];
            var g = rgb[1];
            var b = rgb[2];
            var c = count * 4;
            data[c] = r;
            data[c + 1] = g;
            data[c + 2] = b;
            data[c + 3] = 255;
            count++;
          }
        }
        image.data = data;
        ctx.putImageData(image, 0, 0);
      }
    }
    
    main();
    #canvas {width:500px;height:500px}
    &lt;canvas id="canvas" width=1000 height=1000&gt;&lt;/canvas&gt;

    【讨论】:

      【解决方案2】:

      CSS 的widthheight 与同名画布元素的属性不同——它们只是将画布拉伸到给定的大小(另见this answer)。因此canvas.width === 300canvas.height === 150,默认值。这种低分辨率会给您带来直接的问题。这是您的相同代码,只是正确设置了画布的属性,而不是使用不正确的 css:

      window.onload = function(){
      
          var EQUATION = '';
          var canvas = document.getElementById("canvas");
          var ctx = canvas.getContext('2d');
          var x_min = -3;
          var x_max = 3;
          var y_min = -3;
          var y_max = 3;
          var image = ctx.createImageData(canvas.width, canvas.height);
      
          Complex = function(re, im){
            this.re = re;
            this.im = im;
          }
      
          Complex.prototype.add = function(other){
            return new Complex(this.re+other.re, this.im+other.im);
          }
      
          Complex.prototype.multiply = function(other){
            return new Complex(this.re*other.re-other.im*this.im, this.re*other.im+this.im*other.re);
          }
      
          Complex.prototype.power = function(num){
            var r = this.magnitude();
            var theta = this.angle();
            var a = Math.pow(r, num)*Math.cos(num*theta);
            var b = Math.pow(r, num)*Math.sin(num*theta);
            return new Complex(a, b);
          }
      
          Complex.prototype.magnitude = function(){
            return Math.pow(Math.pow(this.re, 2) + Math.pow(this.im, 2), 0.5);
          }
      
          Complex.prototype.angle = function(){
            return Math.atan2(this.im, this.re);
          }
      
          Complex.prototype.divide = function(other){
            x = new Complex(this.re, this.im);
            y = new Complex(other.re, other.im);
            x = x.multiply(new Complex(other.re, -other.im));
            y = y.multiply(new Complex(other.re, -other.im));
            x = new Complex(x.re/y.re, x.im/y.re);
            return x;
          }
      
          function hslToRgb(h, s, l){ //NOT MY CODE
            var r, g, b;
      
            if(s == 0){
              r = g = b = l; // achromatic
            } else {
              function hue2rgb(p, q, t){
                if(t < 0) t += 1;
                if(t > 1) t -= 1;
                if(t < 1/6) return p + (q - p) * 6 * t;
                if(t < 1/2) return q;
                if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
                return p;
              }
              var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
              var p = 2 * l - q;
              r = hue2rgb(p, q, h + 1/3.0);
              g = hue2rgb(p, q, h);
              b = hue2rgb(p, q, h - 1/3.0);
            }
            return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
          }
      
          function evaluate(i, j){
            var z = new Complex(x_min+j*(x_max-x_min)/canvas.width, y_min+i*(y_max-y_min)/canvas.height);
            var num = z.power(2).add(new Complex(-1, 0)).multiply(z.add(new Complex(-2, -1)).power(2));
            var den = z.power(2).add(new Complex(2, 4));
            var end = num.divide(den);
            var color = end.angle()/(2*Math.PI);
            var brightness = end.magnitude();
            brightness = Math.log(brightness)/Math.log(2) % 1;
            return [color, brightness];
          }
      
          function main(){
            var data = image.data;
            if(EQUATION !== null){
              var count = 0;
              for(var i=0;i<canvas.height;i++){
                for(var j=0;j<canvas.width;j++){
      
                  var c = evaluate(i, j);
                  rgb = hslToRgb(c[0], 1, 0.4+c[1]/5);
                  var r = rgb[0];
                  var g = rgb[1];
                  var b = rgb[2];
                  var c = count*4;
                  data[c] = r;
                  data[c+1] = g;
                  data[c+2] = b;
                  data[c+3] = 255;
                  count++;
                }
              }
              image.data = data;
              ctx.putImageData(image, 0, 0);
          }
        }
      
        main();
      
        function getMousePos(canvas, evt){
          var rect = canvas.getBoundingClientRect();
          return {x: (evt.clientX-rect.left)/(rect.right-rect.left)*canvas.width,
            y: (evt.clientY-rect.top)/(rect.bottom-rect.top)*canvas.height};
        }
      
        document.getElementById("submit").addEventListener("mousedown", function(event){
          EQUATION = document.getElementById("equation").innerHTML;
          var x = main();
        })
      
        document.getElementById("canvas").addEventListener("mousemove", function(event){
          var loc = getMousePos(canvas, event);
          document.getElementById('x').innerHTML = Math.round(loc.x*100)/100;
          document.getElementById('y').innerHTML = Math.round(loc.y*100)/100;
          document.getElementById('brightness').innerHTML = evaluate(loc.y, loc.x)[1];
        })
      
      }
      <input id="equation" type="text">Type Equation</input>
      <canvas id="canvas" width="500" height="500"></canvas>
      <button id="submit">Submit</button><br><span id="x"></span>, <span id="y"></span>, <span id="brightness"></span></p>

      之后,如另一个答案中所述,增加分辨率和“拉伸更小”(一种超级采样)有助于进一步,但不是核心问题。

      【讨论】:

        猜你喜欢
        • 2013-11-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多