cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - ActiveLovehtmlicon-new-collectionicon-personicon-teamlog-outoctocatpop-outspinnerstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

Quick-add: + add another resource

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

Quick-add: + add another resource

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

            
              #app
  .container
    //- サイズの入力
    .row
      .input
        .input-item
          label
            small.text-secondary width:
          .input-group.mr-3
            input(type="number", aria-label="width", placeholder="1920", v-model.number="size.width").form-control
            span.input-group-btn
              button.btn.btn-secondary(type="button", v-on:click.prevent="clearWidth")
                i.fa.fa-trash-o(aria-hidden="true")

        .input-item
          label
            small.text-secondary height:
          .input-group
            input(type="number", aria-label="width", placeholder="1080", v-model.number="size.height").form-control
            span.input-group-btn
              button.btn.btn-secondary(type="button", v-on:click.prevent="clearHeight")
                i.fa.fa-trash-o(aria-hidden="true")

    //- アスペクト比
    .row
      .result
        label
          small.text-secondary AspectRatio
        .aspect-ratio(v-if="canCalc")
          //- 縦横比
          .aspect-ratio__item
            label
              small.text-secondary width : height
            .aspect-ratio__number
              span {{ result.byLong.horizontal }}
              span.ml-1.mr-1 :
              span {{ result.byLong.vertical }}
                    
          //- 短辺基準
          .aspect-ratio__item
            label
              small.text-secondary 1 : height
            .aspect-ratio__number
              span {{ result.byShort.horizontal }}
              span.ml-1.mr-1 :
              span {{ result.byShort.vertical }}
          
        //- 計算できないときの表示
        .aspect-ratio(v-if="!canCalc")
          .aspect-ratio__item
            label
              small.text-secondary width : height
            .aspect-ratio__number.text-muted
              span 0
              span.ml-1.mr-1 :
              span 0
              
          .aspect-ratio__item
            label
              small.text-secondary 1 : height
            .aspect-ratio__number.text-muted
              span 0
              span.ml-1.mr-1 :
              span 0

    //- 履歴
    .row
      .history-header
        label
          small.text-secondary history
          
        button(type="button", v-on:click.prevent="clearHistory", v-bind:disabled="history.length === 0").btn.btn-link.text-muted.history-clear
          i.fa.fa-trash.mr-1(aria-hidden="true")
          | clear
      
      table.table.table-sm.history-table
        thead
          tr
            td #
            td Size
            td(colspan="2") Aspect Ratio
        tbody
          //- tr
          //-   td 1
          //-   td 1920×1080
          //-   td
          //-     span 16:9
          //-     span.result-divider.ml-1.mr-1 /
          //-     span 1.78:1
           
          //- 履歴をリストで表示
          tr(v-for="item in history")
            td {{ item.index }}
            td {{ item.size.width }}×{{ item.size.height }}
            td
              span {{ item.aspectRatio.byLong.horizontal }}:{{ item.aspectRatio.byLong.vertical }}
            td
              span {{ item.aspectRatio.byShort.horizontal }}:{{ item.aspectRatio.byShort.vertical }}
            
          
!
            
              body {
  margin: 20px;
  background-color: #eceff1;
}


.row {
  &:not(:last-of-type) {
    margin-bottom: 20px;
  }
}


.input {
  display: flex;
}


.input-item {
  &:not(:last-of-type) {
    margin-right: 20px;
  }
}

.result {
  display: flex;
  flex-direction: column;
  
  label {
    margin-bottom: 0;
  }
}


.aspect-ratio {
  display: flex;
  border-radius: 4px;
}

.aspect-ratio__item {
  display: flex;
  flex-direction: column;
  
  &:not(:last-of-type) {
    margin-right: 2rem;
  }
}


.aspect-ratio__number {
  font-size: 3rem;
}


.history-header {
  width: 100%;
  display: flex;
  
  label {
    margin-bottom: 0;
  }
}


.history-clear {
  margin-left: auto;  // 右寄せ
}


.history-table {
  thead {
    background-color: transparent;
  }
  
  tbody {
    background-color: #fff;
    
    tr:nth-child(even) {
      background-color: #fafafa;
    }
  }
  
  // 目次
  td:nth-child(1) {
    color: #9e9e9e;
  }
}
            
          
!
            
              // TODO 履歴の削除を1件ごとにできるようにする
// TODO 割り切れないサイズのときに、近いアスペクト比を補足で表示する。数ピクセル違いで、割り切れないサイズのときがある
// 
// 5554x3703 → 5550x3700(3:2)
// 3495x5236 → 5235、3490(2:3)、5238x3492(2:3)
// 主なアスペクト比で総当たりして、差が10以内の候補を出すとか

const value = {
  size: {
    width: NaN,
    height: NaN
  },  // 入力されたサイズ
  history: [],  // アスペクト比の履歴
  historyDelayTimer: NaN  // 履歴に追加するときのディレイ用タイマー
};

const app = new Vue({
  el: '#app',
  data: value,
  
  created: function() {
    // 履歴をクッキーから呼び出す
    const savedHistory = this.loadHistory();
    if (savedHistory) {
      this.history = savedHistory;
    }
  },
            
  methods: {
    
    /**
     * 最大公約数
     *
     * @param {number} a
     * @param {number} b
     * @return 最大公約数
     */
    gcd(a, b) {
      if (b) {
        return this.gcd(b, a % b);
      }
      else {
        return Math.abs(a);
      }
    },
    
    
    /**
     * サイズが入力されているか
     *
     * @param {number} width 幅
     * @param {number} height 高さ
     * @return 入力されていたらtrue
     */
    validateSize(width, height) {
      return _.isNumber(width) && !isNaN(width) && _.isNumber(height) && !isNaN(height);
    },
    
    /**
     * 幅をクリア
     *
     */
    clearWidth() {
      this.size.width = NaN;
    },
    
    /**
     * 高さをクリア
     *
     */
    clearHeight() {
      this.size.height = NaN;
    },
    
    /**
     * 履歴をクッキーに保存
     *
     */
    saveHistory() {
      // カンマ区切りの数字だけにする
      let history = [];
      
      this.history.forEach(function (item, index) {
        history.push([item.index, item.size.width, item.size.height, item.aspectRatio.byLong.horizontal, item.aspectRatio.byLong.vertical, item.aspectRatio.byShort.horizontal, item.aspectRatio.byShort.vertical].join(','));
      });

      Cookies.set('history', history.join('|'));
    },
    
    /**
     * 履歴をクッキーから呼び出す
     *
     * @return 履歴
     */
    loadHistory() {
      let history = [];
      const cookie = Cookies.get('history');
      
      if (cookie) {
        const records = cookie.split('|');  // 1件ごとに区切る
        records.forEach(function (record, index) {
          const items = record.split(',');  // 項目ごとに区切る
          
          // データ形式を復元する
          history.push({
            index: Number(items[0]),
            size: {
              width: Number(items[1]),
              height: Number(items[2])
            },
            aspectRatio: {
              byLong: {
                horizontal: Number(items[3]),
                vertical: Number(items[4])
              },
              byShort: {
                horizontal: Number(items[5]),
                vertical: Number(items[6])
              }
            }
          });
        });
      }
      
      return history;
    },
    
    /**
     * 履歴をクリアする
     *
     */
    clearHistory() {
      // タイマーを止める
      if (!isNaN(this.historyDelayTimer)) {
        clearTimeout(this.historyDelayTimer);
      }
      
      this.history = [];  // 変数をクリア
      Cookies.remove('history');  // Cookieをクリア
    }
  },
  
  computed: {
    
    /**
     * 計算可能か(高さと幅が入力されたか)
     *
     */
    canCalc: function() {
      return this.validateSize(this.size.width, this.size.height);
    },
    
    /**
     * 縦横比
     */
    result: function() {
      let byLong = {
        horizontal: NaN,
        vertical: NaN,
      };
      
      let byShort = {
        horizontal: NaN,
        vertical: NaN,
      }
      
      // 最大公約数を計算して、それで割る
      if (this.validateSize(this.size.width, this.size.height)) {
        // 横基準
        const gcd = this.gcd(this.size.width, this.size.height);
        byLong.horizontal = this.size.width / gcd;
        byLong.vertical = this.size.height / gcd;
        
        // 短辺基準
        if (this.size.width >= this.size.height) {
          // 幅が長い
          byShort.horizontal = this.size.width / this.size.height;
          if (byShort.horizontal.toString().length >= 10) {
            // 割り切れなさそうな値は丸める
            byShort.horizontal = Math.round(byShort.horizontal * 100) / 100;
          }
          byShort.vertical = 1;
        }
        else {
          // 縦が長い
          byShort.vertical = this.size.height / this.size.width;
          if (byShort.vertical.toString().length >= 10) {
            // 割り切れなさそうな値は丸める
            byShort.vertical = Math.round(byShort.vertical * 100) / 100;
          }
          byShort.horizontal = 1;
        }
      }
      
      return {
        byLong,
        byShort
      };
    }
  },
  
  watch: {
    result: function() {
      if (!isNaN(this.historyDelayTimer)) {
        clearTimeout(this.historyDelayTimer);
      }
      
      // サイズが変わってしばらくたったら履歴に追加する
      if (this.canCalc) {
        this.historyDelayTimer = setTimeout(() => {
          this.historyDelayTimer = NaN;
          
          // 履歴に追加
          if (this.history.length === 0 || (!_.isEqual(this.size, this.history[0].size) || !_.isEqual(this.result, this.history[0].aspectRatio))) {
            // 最初の1件 or 前回と違っていたら履歴に追加
            this.history.unshift({
              index: this.history.length + 1,
              size: {
                width: this.size.width,
                height: this.size.height
              },
              aspectRatio: {
                byLong: {
                  horizontal: this.result.byLong.horizontal,
                  vertical: this.result.byLong.vertical
                },
                byShort: {
                  horizontal: this.result.byShort.horizontal,
                  vertical: this.result.byShort.vertical
                }
              }
            });
            
            this.saveHistory();
          }

          // TODO cookieに保存する?
        }, 3000);
      }
    }
  }
});
            
          
!
999px
Loading ..................

Console