| @ブログ

Chart Tooltip

Archive ページにこだわってしまう理由、グラフを表示させてみて(ブログ過去記事をカテゴリーごとに集計してグラフ化 - portal shit!)何となくわかった気がする。過去記事ページというのはブログの書き手にとってはアクティビティダッシュボードなのだ。ジョギング系アプリ使ってる人なら記録をあとから振り返ることがあると思う。それと同じで、自分が過去にどのくらいの頻度、密度でブログを書いていたかを自分は知りたいと思うのだろう。その用途に適しているのが過去記事ページというわけで、 Archive ページの使い勝手の改善は自分のブログアクティビティをふり返りやすくしたいという欲求の表れだったのだと思う。

ちなみにグラフを作ってみたことで、 2006 年頃に異常な数の記事を書いていたことがわかったし、かなり内容のない記事(新聞やテレビを見た感想)ばかり書いていたことがわかって興味深かった。 2007 年からは記事の数が減るが、これは Twitter を使い始めたからだと思う。2009 年頃、病気療養を終えて就職しようとしているあたりからプログラミング関係の記事の数が増え、実家を出て福岡のブラック企業に勤め始めてからは記事数が減った。

あまり ASP 型のブログを使ったことがないのでよく分からないが、こういうカテゴリーごとの Chart 機能はないような気がする。一時期、ダイエット日記を書いていたはてなブログにはアクセス解析機能とアクセス数の統計機能はあったが、自分のブログアクティビティを振り返る機能はないと思う( PRO になればあるのかもしれない)。

自己満足的なブログの書き手にとっては、どれだけ読まれたかももちろん気になるけど、どれだけ書いたかも重要なのではないかと思う。読まれたかというのは結果であり、書いたかというのはプロセスというか、自分の頑張りだ。仕事をする上では結果にフォーカスにしなければ意味がないけど(使われない機能を作ってリリースするのは自己満足)、ブログは趣味なのだからプロセスの部分(自分の頑張り)が可視化される仕組みがあってもよいと思う。

| @技術/プログラミング

Chart

Rechars という React のチャートライブラリを利用して、 Archive ページにカテゴリーごとに記事を集計してグラフ化する機能を作った。

グラフの Bar にカーソルを載せると Tooltip が表示されて、具体的な件数がわかる。

Chart Tooltip

カテゴリごとに表示・非表示を切り替えることも可能。グラフ下のカテゴリー名( Legend )をクリックして切り替えられる。

Chart Show-Hide Toggle

ただし残念なことに Bar を非表示にしたときに Legend の表示を変化させるのが難しくてできていない。

仕事で使ってる Looker とか Redash であれば Legend をクリックして表示・非表示を切り替えることができ、それに連動して Legend の色をトーンダウンさせたりする機能が付属しているが、利用した Recharts にはその機能がなかった。 Bar の表示・非表示切り替えも標準サポートされていなかったので、 GitHub の Issue の情報を頼りに無理矢理実装した。

コードはこんな感じ。結構汚い Hack で、 Bar の表示・非表示を、表示用のキー文字列に空白を追加するかしないかで切り替えている。

  // クリックされたアイテムが `this.state.disabled` 配列の中にすでに存在していれば除外し、
  // 存在してなければ追加する
  selectBar(event) {
    let dataKey = event.dataKey.trim()
    if (this.state.disabled.includes(dataKey)) {
      this.setState({ disabled: this.state.disabled.filter(item => item !== dataKey) })
    } else {
      this.setState({ disabled: this.state.disabled.concat([dataKey]) })
    }
  }

  render() {
    return (
      <ResponsiveContainer height={500}>
        <BarChart
          data={this.state.data}
          margin={{
            top: 20, right: 20, left: 0, bottom: 20,
          }}
          style={{ fontSize: '14px' }}
        >
          <CartesianGrid strokeDasharray="3 3" />
          <XAxis dataKey="year" />
          <YAxis />
          <Tooltip labelStyle={{ color: '#000', fontWeight: 'bold' }} itemStyle={{ margin: '0 2px 0 4px', padding: '0' }} />
          {// Legend クリック時のコールバックに `this.selectBar` を指定する }
          <Legend onClick={this.selectBar} />
          {/*
            `this.state.categories` 配列と `this.state.disabled` 配列の内容を比較し、
            `this.state.disabled` に追加済のカテゴリーは dataKey に空白を追加することで非表示に
          */}
          {this.state.categories.map((category, index) => {
            let dataKey = this.state.disabled.includes(category) ? category + " " : category
            let color = this.colors[index % this.colors.length]
            return(<Bar key={index} dataKey={dataKey} stackId="a" fill={color} />)
          })}
        </BarChart>
      </ResponsiveContainer>
    );
  }

| @技術/プログラミング

react-select-before-after.jpg

一個前の記事のキャプチャにあるように、従来テキストリンクだった年とカテゴリーの選択をセレクトボックスにした。 git log -S したところ去年( 2019 年)の 11 月頃に変更を行ったみたいだ。もうそろそろ 2020 年になろうとしていて、 2005 年からやっていて年の数が 16 個になろうとしていてさすがに多すぎると思ったので整理のためにセレクトボックス化してコンパクトにした。

利用したのは React Select というパッケージで、色々カスタマイズできるみたいだけど面倒だったので素のまま使ってる。

Archives ページのリファクタリング のときのように、子コンポーネントのイベントをトリガーに親コンポーネントの setState() を呼び出すような作りになっている。作るときは結構難儀したけどおかげでスマートフォンで見たときも年やカテゴリーだけでファーストビューが埋まるということがなくなった。

react-select-smartphone-view.jpg

現在のコードをまるっと貼り付けるとこんな感じ↓。

子コンポーネント(年のセレクトボックス)

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import Select from 'react-select'
import PropTypes from 'prop-types'
import { withRouter } from 'react-router-dom'

import history from './history'

class YearSelect extends Component {
  constructor(props) {
    super(props)
    this.state = {
      data: [],
      selectedOption: null
    }
    this.handleChange = this.handleChange.bind(this)
  }

  static propTypes = {
    match: PropTypes.object.isRequired,
    location: PropTypes.object.isRequired,
    history: PropTypes.object.isRequired
  }

  async loadYearSelectFromServer() {
    const request = await fetch('/archives/years.json')
    const response = await request.json()
    this.setState({ data: response })
  }

  componentDidMount() {
    this.loadYearSelectFromServer()
  }

  handleChange(selectedOption) {
    const year = selectedOption ? selectedOption.value : null
    if (year) {
      this.props.history.push(`/archives/${year}`)
    } else {
      this.props.history.push("/archives")
    }
    this.setState(
      { selectedOption },
      () => { this.props.update(year) }
    )
  }

  render() {
    const options = this.state.data.map(year => {
      return { value: year, label: year }
    })
    return (
      <div className="year-list">
        <Select
          value={this.state.selectedOption}
          onChange={this.handleChange}
          options={options}
          placeholder="Year"
          isClearable
        />
      </div>
    )
  }
}

const YearList = withRouter(YearSelect)

export default YearList

親コンポーネント( App.js )

import React, { Component } from 'react'
import { BrowserRouter as Router, Switch, Route, Redirect } from 'react-router-dom'

import YearList from './YearList'
import CategoryList from './CategoryList'
import Archives from './Archives'

class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      category: null,
      year: null,
      length: 0
    }
    this.updateCategory = this.updateCategory.bind(this)
    this.updateYear = this.updateYear.bind(this)
    this.setLength = this.setLength.bind(this)
  }

  updateCategory(category) {
    this.setState({ category })
  }

  updateYear(year) {
    this.setState({ year })
  }

  setLength(length) {
    this.setState({ length })
  }

  render() {
    return(
      <Router history={history}>
        <div className="archive-filter">
          <YearList update={this.updateYear} />
          <CategoryList update={this.updateCategory} activeCategory={this.state.category} />
          <div className="entry-length"><p>{this.state.length} entries</p></div>
        </div>
        <Switch>
          <Route
            exact path="/archives"
            render={(props) =>
              <Archives
                category={this.state.category}
                setLength={this.setLength}
                {...props}
              />
            }
          />
          <Route
            path="/archives/:year(\d{4})"
            render={(props) =>
              <Archives
                category={this.state.category}
                setLength={this.setLength}
                year={this.state.year}
                {...props}
              />
            }
          />
        </Switch>
      </Router>
    )
  }
}

export default App

| @技術/プログラミング

Archives ページ記事絞り込み

Archives ページに記事数を表示するようにした。一昨日、年を未指定の場合は全件読み込むようにしたので Archives ページを訪れると総記事数が確認できるようになった。年やカテゴリーで絞り込むと絞り込み後の件数を確認できる。カテゴリーごとの記事の偏り具合が確認できて便利。

さらにカテゴリーのセレクターは記事件数順でソートするようにした。これまでは categories.id でソートしていたため、件数の多いカテゴリーがセレクトボックスの下の方に埋もれたりしていてあまり良い絞り込み体験ではなかった。

こうしてカテゴリーごとに記事を絞り込んでみてみると、意外と違和感のある分類になっていることに気がつく。記事のカテゴリーに関してはあまり意味ないというか分類が難しいものもあると思う。ブログというカテゴリーに分類してみたものの、実際の記事内容はプログラミング寄りだったりして、読者が読みたい記事を探す際の指針とはなり得ないなと思う。 TF-IDF のような機械的な関連判定が記事のグルーピングには有効だと思う。勝手にカテゴリー付けしてくれたら便利だと思う。

技術的な話をすると、選択したカテゴリーで記事を絞り込む都合上、件数のカウントはフロントエンドで行う必要があった。実は 2 年くらい前の Archives ページにも絞り込み時の件数表示機能はあったのだが、当時は React の機能を活用できておらず、記事の絞り込みは Virtual DOM の操作ではなく CSS の display: none; でやっていて jQuery と大差なかった。記事の絞り込みを React の機能を使ってやるようになってから件数の表示ができていなかったのをできるようにしたのが今回というわけだ。

お爺さんコンポーネントで定義した関数をひ孫コンポーネントまで伝搬させて、結果が変わるとお爺さんコンポーネントの state が書き変わる。ちょっと setState() を使いすぎていて微妙に画面がチラつく( state を更新するたびにレンダリングが行われるため)。もっとうまいやり方があるのかもしれない。

これをやるにあたってこれまで関数として実装していたコンポーネントをクラス化した。 return() の前処理でやっていたゴニョゴニョを componentDidMount() に移す必要が出たが、実は componentDidUpdate() でもやらなければならなかったり、 componentDidMount() 時にはまだ props が渡されてきてないので componentDidUpdate() で実行する必要があったりと、結構ハマりどころがあった。コンポーネントはなるべくクラス化するのが格好いいような気がしていたけど、凝ったことをやる必要がないコンポーネントは関数のままの方がよいと思った。この辺も改めて React はよくできていると感心する。

| @散財

iPhone 11 表面

親父にあげていた iPhone 6 のバッテリーが寿命のようで、 iPhone 7 を譲るために新しい iPhone を買った。 iPhone 11 の 128GB にした。 88000 円くらい。魅惑のオリコローン、 24 回払い分割金利手数料無料で購入した。 iPhone 7 は 2016 年末に買ったのでまるまる 3 年使った。

外観と Face ID

ごっついレンズが複数付いた外観は好きになれなかった。 iPhone 11 Pro は高すぎて買えなかったが外観的にも個人的に受け入れられなかった。 iPhone 11 のレンズ二つの外観もいびつに見えたが、いまさら iPhone XR を買うのは何だかなという感じなので消極的に iPhone 11 を選んだ。

iPhone 11 背面

ケースを付けるかは迷った( iPhone 7 はケースを付けない状態が一番使いやすかった)が、結局付けた。ケースを付けないとレンズの出っ張りが邪魔で、背面を下にしてテーブルに置いたときに安定しない(ガタガタする)。レンズ面の出っ張り対策としてケースは必要だと思った。ケースは Amazon で評価のよかった以下を騙されたと思って買ったけど実際とても良かった(サクラレビューではないようだった)。

ホームボタンと Touch ID のないモデルは初めてで、 Face ID は Apple Pay の支払時に不便かなと懸念していたが、スリープボタンをダブルタップして読み取り機にかざす前に Face ID 認証すればよいのでかえって便利になった。 NFC 認証のスピードも速くなっている気がする(コンビニのレジでもたつくことがなくなった)。

Face ID になってよかったことに、スクリーンプロテクターが全面を覆い隠せるようになったことがある。 Touch ID 方式だとホームボタンは指紋認証のために覆うことができず、スクリーンプロテクターに妙な穴が開いてたりしていて不格好だった。全面を覆えるものを買ったがとても良い。何もはっつけていないみたいに見える。

スクリーンプロテクターは以下のものを買った。貼り付け時に貼り付け位置を失敗しないようにサポートするプラスチックの枠が付いていてスーパー便利だった。これまで位置ずれを気にしながら貼っていたのがバカみたいだった。

重い

iPhone 11 、とにかく重い。調べたところ 194g のようだった1。 iPhone 7 は138g だったみたいなので 56g くらいしか重くなってないのに随分重く感じる。検証機として会社から借りてる Pixel 3a に比べても重い。 50g の差がスマートフォンでは決定的な違いになるんだなと思った。

iPhone 11 と Pixel 3a

電池のもち

重くなったおかげか、バッテリー容量が増えていて電池がとてもよくもつようになった。 iPhone 7 はバッテリーを一度 Apple Store で交換してもらったが、それでもヘビーに使うと一日電池がもたなかった。 iPhone 11 で不意に電池切れになって困った、ということは起こりそうにない。

写真

写真に関しては、日中に撮れる写真の画質は iPhone 7 の頃と大差ないと感じる。もっと綺麗な写真が撮れるかなと思ったけどそうでもなかった。ポートレートモードも試したが、ぼかし処理が甘くて(特に物撮り)被写界深度的に「そこ違うんだけどな」というところがぼけてしまったりする。一眼で撮るのには及ばない。

広角レンズはおもしろい。室内や街並みなどは広角で撮ると楽しい。

街並み

川や道路の大きさ、スケール感が伝わる。

博多川 リバレイン通り

室内

店内の壁一面に手書きのいろんなメニューが貼ってある面白定食屋の様子。店内は広くなく、標準のカメラでは到底壁一面の様子などを写すことはできないが、広角レンズで撮影することができた。

定食屋店内 定食屋店内

ナイトモード

ナイトモードもなかなかおもしろい。 NIKON Z6 を買って最近のカメラの夜景撮影能力に感動していたが、 iPhone 11 のカメラで撮る夜景も大分すごい。以下は近所の山に夜景を撮りに行ったやつの比較。

街の夜景

Z6 で撮った写真は三脚に固定して撮っている。一方 iPhone は手持ち。これはすごい。

今山から見る今宿( NIKON Z6 で撮影)

今山から見る今宿( iPhone 11 で撮影)

夜の踏切

Z6 は ISO 感度が 51200 まで上がってしまってノイズだらけになってしまっている。 iPhone 11 の方は ISO 800 で撮って補正しているのでノイズが少ない。

今宿の踏切( NIKON Z6 で撮影)

今宿の踏切( iPhone 11 で撮影)

こんな感じで、ミラーレスカメラと同等の、状況次第ではミラーレス以上に綺麗な夜景写真が撮れてしまう。画像補正処理技術の向上すごい。


最初は「う〜ん」と思っていた iPhone 11 の外観や重さだけど、使っていくうちに広角レンズの便利さや電池のもちの良さなど、 iPhone 11 の良さがしみじみとわかってきた。値段は iPhone 11 Pro ほどには高くないし、買ってよかったなと思える端末でした。便利。

| @WWW

Temple wall at Hakata

ヒトデさんのブログを読んでGoogleの広告設定を共有してメンバー間でつながるプロジェクトに参加した

Google の広告設定のページにアクセスすると Google からどういう属性として認識されているかがわかり、それを共有して遊ぼうというもの。 Scrapbox はタグ付けが簡単なので人と人の関連性が表現しやすい。

おもしろかったので、普段使ってる三つの Google アカウントでそれぞれでどういう結果になるかやってみた。

個人アカウント

iPhone の Safari でも自宅の Mac でもログインしててよく使うアカウント。ただ最近は Google 検索をあまり使わないようになって、検索には DuckDuck Go を使うようにしている。 #企業向けテクノロジー #業種:_ヘルスケア業界 #業種:_テクノロジー業界 あたりが入っているのがおもしろい。前職が B2B の SaaS / クラウドソーシング企業だったこと、現職がアウトドア関連の企業であることを反映してそう。

#35~44_歳 #男性 #Anova_Culinary #TechAcademy_[テックアカデミー] #Mynavi #Amazon #American_Express #Aha! #Wantedly #Fujitsu #Bic_Camera #Yodobashi_Camera #Kakaku.com #SoftBank_Telecom #Apple_iOS #Mac_OS #SF_映画、ファンタジー映画 #SF_番組、ファンタジー番組 #TV_ゲーム、PC_ゲーム #アウトドア #アクション映画、アドベンチャー映画 #アニメ、漫画 #アメリカン_フットボール #イベント情報 #インディーズ音楽、オルタナティブ_ミュージック #ウェブデザイン、開発 #エクストリーム_スポーツ #オーディオ機器 #オフィス、ビジネスソフトウェア #お祝い、ギフト、祝祭日用グッズ #カメラ #カメラ_レンズ #カメラ、写真機材 #クーポン、割引サービス #クラウドストレージ #クラシック音楽 #グルメ食品、特別食 #クレジット_カード #ゲーム機 #コーヒー_メーカー、エスプレッソ_マシン #コーヒー、紅茶 #コミック、アニメーション #コメディ映画 #コンピュータ_コンポーネント #コンピュータ_ドライブ、ストレージ #コンピュータ_ハードウェア #コンピュータ_モニター、ディスプレイ #コンピュータ、電化製品 #コンピュータ周辺機器 #コンピュータ用メモリ #サイクリング #サッカー #ジャズ #ショッピング #スポーツ #スポーツ衣料 #スマートフォン #ソーシャル_ネットワーク #タブレット_PC #ダンス、電子音楽 #ツアー旅行 #デジタル一眼レフ_カメラ #テレビ、ビデオ、動画 #テレビドラマ #ドキュメンタリー番組、ノンフィクション番組 #トラック、バン、SUV #ニュース #バー、クラブ、ナイトライフ #ハイキング、キャンプ #ハッチバック #ビーチ、島 #ビジネス_サービス #ビジネス_ニュース #ビジュアル_アート、デザイン #ファースト_フード #ファッション、スタイル #フィットネス用品 #フォーク、伝統音楽 #ブランド品、高級品 #ブルース #プログラミング #ペット #ボート #ホームの自動化 #ポップ_ミュージック #マセラティ #ラグビー #ラップトップ、ノートパソコン #ランニング、ウォーキング #リフォーム #レストランのレビュー、予約 #レンタカー、タクシー #ロック_ミュージック #ワールド_ミュージック #飲食店 #映画 #音楽、オーディオ #価格比較 #家庭 #家電 #会計、財務ソフトウェア #確定申告、税務 #学歴:_学士号 #環境に優しい生活、環境問題 #企業向けテクノロジー #業種:_ヘルスケア業界 #業種:_テクノロジー業界 #銀行 #携帯電話 #芸術写真、デジタル_アート #芸能ニュース #個人ブログ、サイト #佐賀 #財務プランニング、マネジメント #山岳、スキー_リゾート #仕事 #子育て、育児 #子供の有無:_子供なし #子供服 #自動車 #自動車販売 #室内装飾、内装 #写真、画像の共有 #写真の印刷サービス #写真編集ソフト #社員数:_中規模雇用者(従業員数:_250~999_人) #授乳用品、離乳食用品 #住宅所有状況:_住宅所有 #書籍、文学 #商品レビュー、価格比較 #食器洗い機 #世界のニュース #世帯収入:_高 #政治 #送金・決済システム、サービス #大学 #都市交通 #投資 #東京 #動画編集ソフトウェア #日本 #配偶者の有無:_既婚 #美容、フィットネス #表計算ソフトウェア #舞台芸術 #福岡 #分散コンピューティング、クラウド_コンピューティング #宝石、アクセサリー #旅行 #料理、レシピ #量販店、デパート #腕時計

趣味アカウント

昔はよく使っていたが最近はあんまり使ってない。主に Mac で使ってた。いまは YouTube でのみこのアカウントを使ってる。

#35~44_歳 #男性 #SF_映画、ファンタジー映画 #SF_番組、ファンタジー番組 #アメリカン_フットボール #イベント情報 #ギター #クイズ番組 #クラシック音楽 #グルメ食品、特別食 #コーヒー、紅茶 #コミック、アニメーション #コンピュータ_ハードウェア #コンピュータ、電化製品 #ショッピング #スポーツ #ソーシャル_ネットワーク #テレビ、ビデオ、動画 #テレビドラマ #トーク番組 #ニュース #バスケットボール #ビジネス_サービス #ビジュアル_アート、デザイン #フィットネス #ヘヴィメタル #ペット #ホームの自動化 #ポップ_ミュージック #ラグビー #リアリティ番組 #リフォーム #ロック_ミュージック #ワールド_ミュージック #映画 #音楽、オーディオ #家族向けテレビ番組 #学校、教室関連用品 #環境に優しい生活、環境問題 #業種:_テクノロジー業界 #芸能ニュース #子供の有無:_3_要素 #自動車 #自動車販売 #室内装飾、内装 #社員数:_小規模雇用者(従業員数:_1~249_人) #住宅所有状況:_住宅所有 #書籍、文学 #商品レビュー、価格比較 #食料品小売業 #世帯収入:_平均以上 #政治 #都市交通 #東アジアの音楽 #配偶者の有無:_既婚 #美容、フィットネス #舞台芸術 #服飾 #分散コンピューティング、クラウド_コンピューティング #野球 #料理、レシピ #量販店、デパート

仕事用アカウント

会社の Gsuite のアカウント。 Chrome の Canary Channel で利用していて、個人アカウントとは明確に使い分けてる。仕事関係の検索やサイト閲覧はほぼほぼこのブラウザーで行っている。仕事用アカウントなので買い物とか趣味の検索はほとんどしない。どうも消費に結びつく検索をしないと世帯収入が低く出る模様。逆に個人アカウントでは頻繁に消費に関係する検索を行っているので世帯収入が高いと判定されてるっぽい。また個人的な用途に使わないので年代を特定できず、 25〜54_歳 という扱いになってるのも面白い。

#25~54_歳 #男性 #Apple_iOS #TV_ゲーム、PC_ゲーム #アウトドア #アクション映画、アドベンチャー映画 #アメリカン_フットボール #エクストリーム_スポーツ #オーディオ機器 #オフィス、ビジネスソフトウェア #カメラ、写真機材 #クーポン、割引サービス #クラウドストレージ #グルメ食品、特別食 #クレジット_カード #コミック、アニメーション #コンピュータ_ハードウェア #コンピュータ、電化製品 #ショッピング #スポーツ #スマートフォン #ソーシャル_ネットワーク #テレビ、ビデオ、動画 #テレビドラマ #ニュース #ハイキング、キャンプ #ビジネス_サービス #ビジュアル_アート、デザイン #ファッション、スタイル #フィットネス #ブランド品、高級品 #ペット #ホームの自動化 #ラグビー #ランニング、ウォーキング #飲食店 #映画 #音楽、オーディオ #価格比較 #家庭 #家電 #学校、教室関連用品 #学歴:_学士号 #企業向けテクノロジー #起業準備 #業種:_テクノロジー業界 #携帯電話 #芸術写真、デジタル_アート #芸能ニュース #犬 #山岳、スキー_リゾート #子供の有無:_子供なし #自動車 #自動車販売 #室内装飾、内装 #社員数:_中規模雇用者(従業員数:_250~999_人) #住宅所有状況:_住宅所有 #書籍、文学 #商品レビュー、価格比較 #世帯収入:_平均以下 #送金・決済システム、サービス #都市交通 #配偶者の有無:_既婚 #美容、フィットネス #舞台芸術 #服飾 #分散コンピューティング、クラウド_コンピューティング #旅行 #料理、レシピ

| @WWW

Santorini sunset

去年の秋( 2018 年 11 月)に、 Flickr が課金ポリシーを変更することがアナウンスされていた。これまですべてのユーザーに無料で与えられていた 1TB のストレージは撤廃され、無料ユーザーは 1000 枚までしか画像をアップロードできないことになった(上限を超えている分は削除される)。料金プランも見直され、既存の Pro ユーザーは $24.95 / 年で Pro プランを利用できていたのが $49.99 / 年(アメリカ国外在住者は $59.99 / 年)と大幅な値上げとなった。 Flickr は 2018 年の 4 月に SmugMug に買収されている。

SmugMug CEO Don MacAskill's statement

Flickr を買収した SmugMug の CEO の Don MacAskill が Flickr のブログで記事を書いている。

要約するとこんな感じ。

  • SmugMug は写真を撮る人にフォーカスしたビジネスを 16 年以上続けてきた
  • 資金調達はしておらず、自己資金で運営されている
  • ユーザーのデータを広告業者に売って広告収入を得ることもしていない
  • 写真家の人々と向き合い、彼らが何を必要としているのかを注意深く聞いてきたのが成功の秘訣
  • そのやり方を Flickr にも適用したい
  • 我々はユーザーのためのサービスであり、投資家や広告会社のためのサービスではない
  • 無料プランは残すが 1000 枚までしかアップロードできず、広告が表示される(プロには表示されない)

要は SmugMug で成功したやり方を Flickr でも踏襲したいということらしい。広告主の為にユーザーを売りたくないと言っておきながら相変わらずフリープランはあって広告を表示するというのは矛盾している気がする。

阿蘇山上から見る普賢岳 / Mount Fugendake from Mount Aso

Flickr VP of Product Andrew Stadlen's statement

Flickr のプロダクト責任者の Andrew Stadlen も無料ユーザーへの扱いの変更について書いている。

  • Yahoo! 子会社時代に無料ユーザーに 1TB のストレージを与えた結果、非常に大きな負の影響があった
  • 無料に釣られて入ってきたユーザーによるコミュニティの破壊
    • 無料のストレージに釣られてくるユーザーは写真に情熱を傾けるユーザーとは属性が異なり、従来 Flickr を特別なものとしていたコミュニティでの交流や関心を共有する場としての雰囲気を後退させた
    • 活気のあるコミュニティを愛する多くの Flickr ユーザーはこのような変化を望まないはずであり、もう一度、写真コミュニティでの交流にフォーカスすることにした
  • 脱広告主優先
    • これまで無料のストレージを提供するために、ユーザーではなく広告主ファーストの状態になっていた
    • Flickr を買収した SmugMug の "You are not our product. You are our priority." というポリシーに共鳴し、広告主ではなく、ユーザーが喜ぶ機能開発を行う
  • Flickr = 無料というイメージの打破
    • 1TB の無料ストレージを与えたことで、ストレージや Flickr 自体が無料のものという認識が広まってしまった
    • 活発に利用しているユーザーにサイトの安定運営と成長、イノベーションを継続するための支援をしてほしい

Nagatare beach

Pro プラン値上げに対する Flickr ユーザーの反応

ヘルプフォーラム内に上の Andrew Stadlen が既存ユーザー向けの料金プランを改定する旨を書いていて炎上している( 1300 個以上レスがついている)。

Pro であることで得られるメリットは広告なしブラウズとアクセス解析と制限なしのアップロードだが、いまでも値段に見合わないと思いながら払っている、という書き込みがほとんどで、このままメリットが増えない状態で値上げされるんだったら更新しない、という書き込みが目立つ。

この説明では結局なぜ値上げをするのかが書かれておらず、「2年は値上げしないという約束を守ったんで上げますね」ということしか書かれていない。 SmugMug の最安料金プランが $48 / 年なのでそれに合わせるように SmugMug から指示されたのだと思う。

DSC_6062


考察

Flickr が直面している事業領域は、写真共有(家族・友人)、クラウドストレージ、 SNS という三つだと思う。今日ではそれぞれの市場に強豪がひしめいていてかなり厳しそうだが、昔はそうではなかったはずで、 Flickr が Yahoo! に買収された 2005 年頃は以下のような感じだったはずだ。

Photo Sharing Product Market 2005

Photo Sharing Product Market 2005

この頃は iPhone も Android もなかったし、 iCloud も Picasa Web Album も Google Photos も、 Facebook も Twitter も Instagram もなかった。家族と写真を共有するには CD や DVD に写真を「焼いて」手渡すか、メールに縮小した画像をちまちま添付して送るしかなかった。そこに Flickr が登場して、インターネット越しに簡単に家族や友人に対して写真を共有できるようになった。煩わしいリサイズのことは考えなくてもよい。

家族や友人と共有するだけではなく、写真をインターネット全体に公開して見ず知らずの人と交流することもできた。この頃、写真を公開して人々の反応を得ることができる SNS は Flickr だけだった。他のサービスは家族・友人間の共有やクラウドストレージの機能を売りにしていたが、見ず知らずの人と写真を通してつながれるのは Flickr だけで、そこが写真愛好家の心を捉えた。共有範囲をコントロールできる仕組みと SNS 機能(コミュニティ)によって初期の Flickr は成長した。

その勢力図がいまでは以下のようになってしまった。

Photo Sharing Product Market 2019

Photo Sharing Product Market 2019

スマートフォンが普及して人々がスマートフォンで写真を撮るようになると、WhatsApp や LINE のようなメッセンジャーで写真が共有されるようになった。 日常的に利用しているチャットアプリでの写真共有は Flickr よりもはるかに便利で、身近な人に写真を共有できる Flickr の強みはコモディティ化し、強みではなくなってしまった。

クラウドストレージ市場には Apple や Google がそれぞれ iCloud Photo Stream と Google Photos を投入し、 iPhone ユーザーと Android ユーザーはデフォルトでこれらのサービスを利用することになった。 OS に統合されたこれらのクラウドストレージの利用にはアプリのインストールすら不要でユーザーが深く意識することなくクラウドストレージに写真が保存されるようになった。おまけに Dropbox や Evernote 、 Amazon Drive などの競合もこの市場に参入し、瞬く間にレッドオーシャンと化してしまった。 Google Photos がローンチした 2015 年から Flickr への画像アップロード数が減少し始めている(How many photos are uploaded monthly to Flickr? Flickr may… | Flickr)。

SNS としての Flickr の強みも揺らいでくる。普段使っている SNS で写真を公開できるようになると、わざわざ Flickr を使う必要がない。 Facebook や Twitter で写真を共有できるならわざわざほかの SNS を使う理由はなくなるだろう。写真をキーにオンラインで人とコミュニケーションするときに、 Flickr を使う必要がなくなった。

極めつけは Instagram の登場で、まだスマートフォンで撮られた写真の画質が悪い頃からフィルターをかけるという機能を呼び水に人を集め、スマートフォンによる写真共有 SNS の代表格として君臨した。ハイアマチュアの人々が撮影した綺麗すぎる写真を敬遠して普通のスマートフォンユーザーは Instagram を使ったのだ、と書いている人もいた。

In recent times the nature of photography has shifted quite far from what it used to be. Ten years ago, both amateur and professional photographers were focusing on taking breath-taking pictures of landscapes, people, nature and different environments and cultures. These days people are taking pictures of their cats, their shoes and their meals.

Why is Flickr not Growing as Quickly as Instagram? - Flickr Embed

この辺はバカと暇人の話に通じるのかもしれない。インターネットがバカになって Flickr が廃れ、 Instagram が流行った。超絶美麗な風景写真よりも自撮り画像とかメシ写真の方が人目を引くのだろう。

こうして Flickr は写真共有、クラウド画像ストレージ、 SNS のすべての市場において強みを失ってしまった。

深江海岸の夕焼け

収益モデル

収益モデルを検証する。 Flickr の収益モデルは 3 つあって、 Ad (無料ユーザー向け)と Subscription ( Pro ユーザー向け)と Merchandise (写真のプリント)だ。

Revenue Model

Flickr 同様に広告モデルを採用している Instagram や Facebook は、写真好きではない普通の人の生活に深く入り込んでおり、 Flickr の何倍もの PV を集めているはずだ。広告による収益モデルはとにかく PV を集めてインプレッションを増やすことが重要なのでこの分野で Flickr は苦戦することが予想される。しかも無料ユーザーに対する制限を強化するとなれば、益々広告収入の額は少なくなっていくだろう。

サブスクリプション領域で最大の競合である iCloud や Google Photos をやっている Apple と Google はハードウェアが売れたり iOS / Android のプラットフォームが拡大することが目的なので、サブスクリプションモデルではあるがぶっちゃけ単体で儲からなくても大丈夫なはずだ。 Flickr や SmugMug は単体で利益を上げることを重視して運営しなければならないので、いわゆる捨て身の状態で運営できる iCloud や Google Photos とまともに勝負するとかなり苦戦を強いられるだろう。

写真のプリント販売に関しては、この先は厳しくなっていくばかりだろう。むかし iPhoto にあったプリント販売を利用したことがあるがいまいちだったし、もはや Photos にはそんな機能は付いてない( App Store でプリントできるアプリを探しましょうと表示される)。印刷したければ印刷サービスを使えばいいだけで、わざわざ中間マージンを取られる Flickr を使うメリットがない。そもそもプリントした写真を好むユーザーは Flickr を使うとは思えない。高齢者はプリントを好むかもしれないが、 Flickr の主なユーザー層はミレニアム世代以下の若い世代だろう。

どの領域においても Flickr が儲けられる要素が見つからない。

今宿夕焼け

課金ポリシー

個人的には $44.95 / 2 年 で Flickr Pro を利用していたので今回の値上げはかなり高いなという印象だった。アメリカ国外居住者に対しては $49.99 ではなく $10 上乗せした $59.99 を提示するなど料金プランには不公平感がある。

さらに運営にかかるコストを、コミュニティに貢献し最もアクティブに使っているユーザーから徴収するというのもいい印象を持てない。リポジトリに課金していた頃の GitHub が OSS には無料で利用させていたように、コミュニティに活気をもたらすユーザーはむしろ優遇しなければならないのではないかと思う。熱心に良い写真を投稿し、 Flickr の PV や MAU 増に貢献する Flickr にとってありがたいユーザー(プラットフォーム革命でいうところの生産者)に課金して、ただ見に来るだけのユーザー(消費者)は無料で使えるのには違和感がある。

あるいは Flickr は写真を投稿せず見に来るだけのユーザーこそコミュニティに活気をもたらすと考えているのだろうか。 SmugMug CEO の Don MacAskill のブログ記事には「無料ユーザーがコミュニティに活気をもたらす」と書いてあったので、そういうスタンスなのかもしれない。写真を投稿するユーザーはいいねが欲しく、いいねをもらうために Flickr にお金を払って Pro ユーザーになり写真をアップロードする。すると無料で利用している閲覧ユーザーたちがやってきていいねを押していく。いいねをもらった Pro ユーザーは承認欲求をみたされる。この考え方ではいいねを送る方が生産者であり、受け取る(写真をアップロードする)方が消費者だといえるのだろう。そうなると写真をアップロードする側に課金するポリシーは分からなくもない。

ただ一方で VP of Product の Andrew Stadlenは「無料の 1TB ストレージにつられてやってきたユーザーはコミュニティを破壊した」とも述べている。いいねをもらったり送ったりを Pro ユーザー同士で行わせたいのだろうか。誰もが消費者であり誰もが生産者である eBay や Etsy のような市場構造にしたいのかもしれない。ただそれでは市場(コミュニティ)はしぼんでしまうだろう。

DSC_1021

SmugMug way は Flickr に適合するか

Flickr は家族や友だちと写真を共有する他に、 SNS 上で写真をベースに交流するという側面があった。プロではない、普通の写真好きな人たちが撮った写真を披露しあい、お互いの写真を褒め合う場所。上の方で引用した記事にもそのようなことが書かれている。

一方で SmugMug は交流というよりも、写真を生業としている人たちのためのオンラインストレージで、無料プランはなく、利用するには必ず費用を払う必要があった。収益モデルこそ似ていて Subscription モデルだが、そもそものビジネスモデルが異なる。 Flickr はプラットフォームだが、 SmugMug はプラットフォームではない。プラットフォーム革命の定義にならうなら、 SmugMug はネットワーク効果の働かない単なる SaaS で、 20 世紀型の直線的ビジネスモデルに過ぎない。

SmugMug による Flickr 買収はきっとうまく行かないだろう。 The Verge の記事には以下のように書いてある。

But technology-wise, this acquisition might be a tall order for SmugMug, which isn’t nearly as big as the photo service it now owns

Flickr acquired by professional photo hosting service SmugMug - The Verge

サービス規模的には Flickr の方がでかいので、技術的には SmugMug には手に負えない仕事を請け負ってしまったことになりそうだ。


Flickr は単独で収益モデルを確立する前に Yahoo! に買収されて飼い殺し状態が長かったことが悔やまれる。正しくスマートフォン時代に適応できていたら、今頃は女子高生が利用するのは Instagram ではなく Flickr だったのかもしれない。

10 年前に書いた記事にこんなことを書いていた。

2007年の春、僕は病気で入院してた。抗がん剤やってたから自由に外を出歩くことは出来なかった(抗がん剤の副作用で免疫力が極端に低下し、すぐ風邪をひくから。免疫力が低下している状態で風邪をひくとすぐに肺炎になり、死に至る)。せっかく辺りに桜が咲いているのに、それを見に行くことも出来ない。でもFlickrがあった。僕はWILLCOMの遅い回線でFlickrにアクセスし、「桜」で検索した。健康な人達がアップロードした桜の写真を見ることが出来た。2007年春のささやかな花見でした。

Photo publication for the rest of us

さようなら、これまでありがとう。