SlideShare une entreprise Scribd logo
1  sur  30
Télécharger pour lire hors ligne
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
React.jsで
広告テンプレートを
作りたい
リッチラボ株式会社
穴井宏幸(@pirosikick)
!
SCRIPTY#3 2015/03/10
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
React.jsで
広告テンプレートを
作りたい
リッチラボ株式会社
穴井宏幸(@pirosikick)
!
SCRIPTY#3 2015/03/10
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
React.jsで
広告テンプレートを
作りたい
リッチラボ株式会社
穴井宏幸(@pirosikick)
!
SCRIPTY#3 2015/03/10
リッチ広告
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
穴井 宏幸
リッチラボ株式会社 エンジニア
@pirosikick
(ぴろしきっく)
JavaScript, React.js, Flux
Golangを始めたい
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
話すこと
• リッチ広告でReact.jsを使いたい
• 検証のためいくつか書きなおした
• よかったことや
• あんまりよくなかったこと・実戦投入への課題など
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
/**
* "Hello! ${ 入力内容 }"と表示するだけのサンプル
*/
import React from "react";
!
let HelloApp = React.createClass({
getInitialState () {
return { name: this.props.defaultName || '' };
},
!
render () {
return (
<div className="wrapper">
<h1>Hello! { this.state.name }</h1>
<input type="text" onChange={this.onChange}/>
</div>
);
},
!
onChange (e) {
this.setState({ name: e.target.value });
}
});
!
React.render(<HelloApp defaultName="pirosikick" />, document.body);
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
• View専門
• Virtual-DOM
• JSX(別に使わなくても書けるけども)
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
リッチ広告
ちょっとデモ
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
リッチ広告
• ユーザイベントで変化させてリッチな感じに
• ex) scroll, deviceorientation, touch, etc…
• CTRや広告の印象が良かったりする
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
なんでReactで
作りたいか
• DOMを組み立てていく様が普段の開発と似ていて

なんか相性良さそう
• 単純に新しいことをやりたい
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
React.jsで
書き直してみた
バナープラス
スクロールしている間だけ大きく表示される
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
比較
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
Before 163行
(独自ライブラリ除く)
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
'use strict';
'use es6';
!import React, { PropTypes } from 'react';
!let Banner = React.createClass({
propTypes: {
src: PropTypes.string.isRequired,
width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
margin: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
},
!render () {
let props = this.props;
let style = {
width: props.width,
height: props.height,
margin: props.margin || '0 auto',
position: 'relative',
background: `transparent url(${props.src}) no-repeat 50% 50%`,
backgroundSize: 'contain'
};
!return <div style={style}>{props.children}</div>;
}
});
!let Extension = React.createClass({
propTypes: {
src: PropTypes.string.isRequired,
width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
offsetTop: PropTypes.number.isRequired,
offsetLeft: PropTypes.number.isRequired,
shown: PropTypes.bool
},
!render () {
let props = this.props;
let style = {
width: props.width,
height: props.height,
position: 'absolute',
top: props.offsetTop,
left: props.offsetLeft,
zIndex: 100000,
opacity: props.shown ? 1 : 0,
background: `transparent url(${props.src}) no-repeat 50% 50%`,
backgroundSize: 'contain',
pointerEvents: 'none',
webkitTransition: '-webkit-transform 0s linear',
webkitTransform: 'translate3d(0, 0, 0)',
webkitTransitionDelay: `${props.show ? 0 : 200}ms`
};
!return <div style={style}></div>;
}
});
!let Anchor = React.createClass({
propTypes: {
href: PropTypes.string.isRequired,
width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
clickTracking: PropTypes.string
},
!render () {
let props = this.props;
let style = {
position: 'absolute',
width: props.width,
height: props.height,
top: 0,
left: 0
};
!return <a href={props.href} onClick={this.onClick}></a>;
},
!onClick () {
if (this.props.clickTracking) {
var img = new Image();
img.src = this.props.clickTracking;
}
}
});
!export default React.createClass({
displayName: 'BannerPlus',
!propTypes: {
param: PropTypes.object.isRequired,
option: PropTypes.object
},
!getInitialState () {
let param = this.props.param;
let option = this.props.option || {};
let state = {};
state.link = param.link;
state.width = option.width || 320;
state.height = option.height || 100;
state.banner = {
src: param['banner'],
margin: option['margin'] || '0 auto'
};
state.extension = {
src: param['extension'],
width: option['extensionWidth'] || 320,
height: option['extensionHeight'] || 200,
shown: false,
};
state.extension.offsetTop = -(state.extension.height - state.height) * 0.5;
state.extension.offsetLeft = -(state.extension.width - state.width) * 0.5;
state.clickTracking = option['clickTracking'];
state.researchTracking = option['researchTracking'] || [];
!return state;
},
!render () {
let state = this.state;
!return (
<Banner
src={state.banner.src}
width={state.width}
height={state.height}
margin={state.banner.margin}>
! <Anchor
href={state.link}
width={state.width}
height={state.height}></Anchor>
! <Extension
src={state.extension.src}
width={state.extension.width}
height={state.extension.height}
offsetTop={state.extension.offsetTop}
offsetLeft={state.extension.offsetLeft}
shown={state.extension.shown}></Extension>
! <div ref="hoge"></div>
</Banner>
);
},
!showExtension () {
if (!this.state.extension.shown) {
this.state.extension.shown = true;
this.setState({ extension: this.state.extension });
}
},
!hideExtension () {
if (this.state.extension.shown) {
this.state.extension.shown = false;
this.setState({ extension: this.state.extension });
}
},
!showExtensionDuringScroll: (function() {
let timerId;
return function () {
this.showExtension();
! if (timerId) clearInterval(timerId);
! timerId = setInterval(this.hideExtension, 200);
}
})(),
!componentDidMount () {
document.body.addEventListener('touchmove', this.showExtensionDuringScroll);
window.addEventListener('scroll', this.showExtensionDuringScroll);
},
!componentWillUnmount () {
document.body.removeEventListener('touchmove', this.showExtensionDuringScroll);
window.removeEventListener('scroll', this.showExtensionDuringScroll);
}
});
After 185行
(本体除く)
• ちょっと増えた
• 体感的にはそんなには書いていない
• propTypesの記述
• JSXを読みやすくするために

改行を多く入れたのが原因?
• 実際はrequireとかで分けるだろう
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
イベント処理記述
設定に基づきDOM構築
• ユーザイベントで

style属性を書き換えるような処理
Before 全体の構成
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
/**
* DOMの構築
*/
function $elem (elemName) { return $(document.createElement(elemName)); }
!
// CSSは使えないので直接style属性に定義
var banner $elem('div').css({
'width': config.width,
'height': config.height,
'margin': config.margin,
'position': 'relative',
'background': `transparent url(${config.banner.src}) no-repeat 50% 50%`,
'backgroundSize': 'contain'
});
!
var extension = $elem('div').css(/* 割愛 */);
!
var anchor = $elem('a').css(/* 割愛 */);
!
anchor
.attr('href', config.link)
.on('click', function () { ... };
!
banner.append(extension, anchor);
Before
※注) 実際のコードは晒せないのでjQueryで似たようなコードを書きました
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
import React from "react";
!
let Banner = React.createClass({
propTypes: {/* 割愛 */},
!
render () {
let props = this.props;
let style = {
width: props.width,
height: props.height,
margin: props.margin || '0 auto',
position: 'relative',
background: `transparent url(${props.src}) no-repeat 50% 50%`,
backgroundSize: 'contain'
};
!
return <div style={style}>{props.children}</div>;
}
});
!
// 上とstyle以外ほとんど一緒なので割愛
let Extension = React.createClass({/* 割愛 */});
let Anchor = React.createClass({/* 割愛 */});
After
• そんなに変わらない
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
let BannerPlus = React.createClass({
propTypes: { param: ..., option: ... },
!
// this.propsに来た設定値の初期化・Validationなど
getInitialState () {
let param = this.props.param;
let option = this.props.option;
return { link: param.link, banner: { ... }, extension: { ... } };
},
!
render () {
let state = this.state;
return (
// this.stateを子Componentに渡してDOMを構築する
<Banner src={state.banner.src} width={...} height={...}>
<Anchor href={state.link} width={...} height={...} />
<Extension isShown={state.isExtensionShown} src={...} width={...} height={...} />
</Banner>
);
},
!
// 外側の大きいバナーの表示・非表示
showExtension () {/* 後述 */},
hideExtension () {/* 後述 */}
});
!
// 広告描画
var param = { ... }, option = { ... }
React.render(<BannerPlus param={param} option={option} />, target);
After
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
※注) 実際のコードは晒せないのでjQueryで似たようなコードを書きました
Before
/**
* ユーザイベントに合わせてstyle属性の書き換え
*/
!
var isExtensionShown = false;
var timerId;
!
// 大きいバナーを表示する処理
function showExtension () {
extension.css({ /* 割愛 */ });
isExtensionShown = true;
}
!
// 多きバナーを隠す処理
function hideExtension () {
dom.extension.css({ /* 割愛 */ });
isExtensionShown = false;
}
!
// スクロール時に表示・非表示
$(window).on('scroll', function () {
!isExtensionShown && showExtension();
!
if (timerId) clearInterval(timerId);
!
timerId = setInterval(function () {
hideExtension();
}, 200);
});
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
let BannerPlus = React.createClass({
render () { /* 割愛 */ },
!
// state.extension.shownの切り替えで
// 子Componentのstyleを切り替える
showExtension () {
if (!this.state.isExtensionShown) this.setState({ isExtensionShown: true });
},
!
hideExtension () {
if (this.state.isExtensionShown) this.setState({ isExtensionShown: false });
},
!
onScroll () {
!this.state.isExtensionShown && this.showExtension();
!
if (this._timerId) clearInterval(this._timerId);
!
this._timerId = setInterval(function () {
this.hideExtension();
}.bind(this), 200);
}
!
componentDidMount () {
window.addEventListener('scroll', this.onScroll);
},
!
componentWillUnmount () {
window.removeEventListener('scroll', this.onScroll);
}
});
After
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
/**
* 外側の大きいバナーのComponent
*/
!
let Extension = React.createClass({
propTypes: { /* 割愛 */ },
!
render () {
let props = this.props;
let style = {
!
/* 省略 */
!
/**
* props.isShownで表示・非表示
*/
opacity: props.isShown ? 1 : 0,
!
/* 省略 */
!
};
!
return <div style={style}></div>;
}
});
After
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
良い点
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
• JSX HTMLを書く感じで心地良い
• 全体の見通しがよくなった
• Componentに見た目と振る舞いの定
義があるから
• 単体テストしやすい
• this.props
• React.addons.TestUtils
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
実戦投入への課題
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
うまく
実装できなかったこと
• 広告を挿入する要素の外側への処理
• ex) expand時にbodyに要素を付け替え
• 実装できてもあんまりいいやり方では無さそう
• 2回React.renderを呼び、予めbodyに要素追加
• Reactの外から要素を移動して戻す
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
サイズがデカイ
• react.min.js v0.12.2 128KB
• 大体10KBに収まるように心がけているので、

めっちゃでかい。。。
• 他のVDOM系もそれなりのサイズ感
• deku.min.js 9.9KB
• virtual-dom 28KB(uglifyjs)
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
まとめ
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
• リッチ広告への実戦投入は

現状出来なそう🙅
• 容量が小さいものが欲しい
• 書いている時の気持ちよさ👏

(JSXに限る)
• コードの見通しがよく感じた👍
©2015 Rich Lab Co., Ltd. All Rights Reserved.
無断利用・転載禁止
Thanks:)
@pirosikick
(ぴろしきっく)

Contenu connexe

Plus de Yahoo!デベロッパーネットワーク

ヤフーでは開発迅速性と品質のバランスをどう取ってるか
ヤフーでは開発迅速性と品質のバランスをどう取ってるかヤフーでは開発迅速性と品質のバランスをどう取ってるか
ヤフーでは開発迅速性と品質のバランスをどう取ってるかYahoo!デベロッパーネットワーク
 
データの価値を最大化させるためのデザイン~データビジュアライゼーションの方法~ #devsumi 17-E-2
データの価値を最大化させるためのデザイン~データビジュアライゼーションの方法~ #devsumi 17-E-2データの価値を最大化させるためのデザイン~データビジュアライゼーションの方法~ #devsumi 17-E-2
データの価値を最大化させるためのデザイン~データビジュアライゼーションの方法~ #devsumi 17-E-2Yahoo!デベロッパーネットワーク
 
ヤフーを支えるセキュリティ ~サイバー攻撃を防ぐエンジニアの仕事とは~ #yjtc
ヤフーを支えるセキュリティ ~サイバー攻撃を防ぐエンジニアの仕事とは~ #yjtcヤフーを支えるセキュリティ ~サイバー攻撃を防ぐエンジニアの仕事とは~ #yjtc
ヤフーを支えるセキュリティ ~サイバー攻撃を防ぐエンジニアの仕事とは~ #yjtcYahoo!デベロッパーネットワーク
 
Yahoo! JAPANのIaaSを支えるKubernetesクラスタ、アップデート自動化への挑戦 #yjtc
Yahoo! JAPANのIaaSを支えるKubernetesクラスタ、アップデート自動化への挑戦 #yjtcYahoo! JAPANのIaaSを支えるKubernetesクラスタ、アップデート自動化への挑戦 #yjtc
Yahoo! JAPANのIaaSを支えるKubernetesクラスタ、アップデート自動化への挑戦 #yjtcYahoo!デベロッパーネットワーク
 
ヤフーのAIプラットフォーム紹介 ~AIテックカンパニーを支えるデータ基盤~ #yjtc
ヤフーのAIプラットフォーム紹介 ~AIテックカンパニーを支えるデータ基盤~ #yjtcヤフーのAIプラットフォーム紹介 ~AIテックカンパニーを支えるデータ基盤~ #yjtc
ヤフーのAIプラットフォーム紹介 ~AIテックカンパニーを支えるデータ基盤~ #yjtcYahoo!デベロッパーネットワーク
 
新技術を使った次世代の商品の見せ方 ~ヤフオク!のマルチビュー機能~ #yjtc
新技術を使った次世代の商品の見せ方 ~ヤフオク!のマルチビュー機能~ #yjtc新技術を使った次世代の商品の見せ方 ~ヤフオク!のマルチビュー機能~ #yjtc
新技術を使った次世代の商品の見せ方 ~ヤフオク!のマルチビュー機能~ #yjtcYahoo!デベロッパーネットワーク
 
PC版Yahoo!メールリニューアル ~サービスのUI/UX統合と改善プロセス~ #yjtc
PC版Yahoo!メールリニューアル ~サービスのUI/UX統合と改善プロセス~ #yjtcPC版Yahoo!メールリニューアル ~サービスのUI/UX統合と改善プロセス~ #yjtc
PC版Yahoo!メールリニューアル ~サービスのUI/UX統合と改善プロセス~ #yjtcYahoo!デベロッパーネットワーク
 
モブデザインによる多職種チームのコミュニケーション改善 #yjtc
モブデザインによる多職種チームのコミュニケーション改善 #yjtcモブデザインによる多職種チームのコミュニケーション改善 #yjtc
モブデザインによる多職種チームのコミュニケーション改善 #yjtcYahoo!デベロッパーネットワーク
 
ユーザーの地域を考慮した検索入力補助機能の改善の試み #yjtc
ユーザーの地域を考慮した検索入力補助機能の改善の試み #yjtcユーザーの地域を考慮した検索入力補助機能の改善の試み #yjtc
ユーザーの地域を考慮した検索入力補助機能の改善の試み #yjtcYahoo!デベロッパーネットワーク
 

Plus de Yahoo!デベロッパーネットワーク (20)

ゼロから始める転移学習
ゼロから始める転移学習ゼロから始める転移学習
ゼロから始める転移学習
 
継続的なモデルモニタリングを実現するKubernetes Operator
継続的なモデルモニタリングを実現するKubernetes Operator継続的なモデルモニタリングを実現するKubernetes Operator
継続的なモデルモニタリングを実現するKubernetes Operator
 
ヤフーでは開発迅速性と品質のバランスをどう取ってるか
ヤフーでは開発迅速性と品質のバランスをどう取ってるかヤフーでは開発迅速性と品質のバランスをどう取ってるか
ヤフーでは開発迅速性と品質のバランスをどう取ってるか
 
オンプレML基盤on Kubernetes パネルディスカッション
オンプレML基盤on Kubernetes パネルディスカッションオンプレML基盤on Kubernetes パネルディスカッション
オンプレML基盤on Kubernetes パネルディスカッション
 
LakeTahoe
LakeTahoeLakeTahoe
LakeTahoe
 
オンプレML基盤on Kubernetes 〜Yahoo! JAPAN AIPF〜
オンプレML基盤on Kubernetes 〜Yahoo! JAPAN AIPF〜オンプレML基盤on Kubernetes 〜Yahoo! JAPAN AIPF〜
オンプレML基盤on Kubernetes 〜Yahoo! JAPAN AIPF〜
 
Persistent-memory-native Database High-availability Feature
Persistent-memory-native Database High-availability FeaturePersistent-memory-native Database High-availability Feature
Persistent-memory-native Database High-availability Feature
 
データの価値を最大化させるためのデザイン~データビジュアライゼーションの方法~ #devsumi 17-E-2
データの価値を最大化させるためのデザイン~データビジュアライゼーションの方法~ #devsumi 17-E-2データの価値を最大化させるためのデザイン~データビジュアライゼーションの方法~ #devsumi 17-E-2
データの価値を最大化させるためのデザイン~データビジュアライゼーションの方法~ #devsumi 17-E-2
 
eコマースと実店舗の相互利益を目指したデザイン #yjtc
eコマースと実店舗の相互利益を目指したデザイン #yjtceコマースと実店舗の相互利益を目指したデザイン #yjtc
eコマースと実店舗の相互利益を目指したデザイン #yjtc
 
ヤフーを支えるセキュリティ ~サイバー攻撃を防ぐエンジニアの仕事とは~ #yjtc
ヤフーを支えるセキュリティ ~サイバー攻撃を防ぐエンジニアの仕事とは~ #yjtcヤフーを支えるセキュリティ ~サイバー攻撃を防ぐエンジニアの仕事とは~ #yjtc
ヤフーを支えるセキュリティ ~サイバー攻撃を防ぐエンジニアの仕事とは~ #yjtc
 
Yahoo! JAPANのIaaSを支えるKubernetesクラスタ、アップデート自動化への挑戦 #yjtc
Yahoo! JAPANのIaaSを支えるKubernetesクラスタ、アップデート自動化への挑戦 #yjtcYahoo! JAPANのIaaSを支えるKubernetesクラスタ、アップデート自動化への挑戦 #yjtc
Yahoo! JAPANのIaaSを支えるKubernetesクラスタ、アップデート自動化への挑戦 #yjtc
 
ビッグデータから人々のムードを捉える #yjtc
ビッグデータから人々のムードを捉える #yjtcビッグデータから人々のムードを捉える #yjtc
ビッグデータから人々のムードを捉える #yjtc
 
サイエンス領域におけるMLOpsの取り組み #yjtc
サイエンス領域におけるMLOpsの取り組み #yjtcサイエンス領域におけるMLOpsの取り組み #yjtc
サイエンス領域におけるMLOpsの取り組み #yjtc
 
ヤフーのAIプラットフォーム紹介 ~AIテックカンパニーを支えるデータ基盤~ #yjtc
ヤフーのAIプラットフォーム紹介 ~AIテックカンパニーを支えるデータ基盤~ #yjtcヤフーのAIプラットフォーム紹介 ~AIテックカンパニーを支えるデータ基盤~ #yjtc
ヤフーのAIプラットフォーム紹介 ~AIテックカンパニーを支えるデータ基盤~ #yjtc
 
Yahoo! JAPAN Tech Conference 2022 Day2 Keynote #yjtc
Yahoo! JAPAN Tech Conference 2022 Day2 Keynote #yjtcYahoo! JAPAN Tech Conference 2022 Day2 Keynote #yjtc
Yahoo! JAPAN Tech Conference 2022 Day2 Keynote #yjtc
 
新技術を使った次世代の商品の見せ方 ~ヤフオク!のマルチビュー機能~ #yjtc
新技術を使った次世代の商品の見せ方 ~ヤフオク!のマルチビュー機能~ #yjtc新技術を使った次世代の商品の見せ方 ~ヤフオク!のマルチビュー機能~ #yjtc
新技術を使った次世代の商品の見せ方 ~ヤフオク!のマルチビュー機能~ #yjtc
 
PC版Yahoo!メールリニューアル ~サービスのUI/UX統合と改善プロセス~ #yjtc
PC版Yahoo!メールリニューアル ~サービスのUI/UX統合と改善プロセス~ #yjtcPC版Yahoo!メールリニューアル ~サービスのUI/UX統合と改善プロセス~ #yjtc
PC版Yahoo!メールリニューアル ~サービスのUI/UX統合と改善プロセス~ #yjtc
 
モブデザインによる多職種チームのコミュニケーション改善 #yjtc
モブデザインによる多職種チームのコミュニケーション改善 #yjtcモブデザインによる多職種チームのコミュニケーション改善 #yjtc
モブデザインによる多職種チームのコミュニケーション改善 #yjtc
 
「新しいおうち探し」のためのAIアシスト検索 #yjtc
「新しいおうち探し」のためのAIアシスト検索 #yjtc「新しいおうち探し」のためのAIアシスト検索 #yjtc
「新しいおうち探し」のためのAIアシスト検索 #yjtc
 
ユーザーの地域を考慮した検索入力補助機能の改善の試み #yjtc
ユーザーの地域を考慮した検索入力補助機能の改善の試み #yjtcユーザーの地域を考慮した検索入力補助機能の改善の試み #yjtc
ユーザーの地域を考慮した検索入力補助機能の改善の試み #yjtc
 

Dernier

業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)Hiroshi Tomioka
 
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。iPride Co., Ltd.
 
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアルLoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアルCRI Japan, Inc.
 
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
LoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイスLoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイス
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイスCRI Japan, Inc.
 
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。iPride Co., Ltd.
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NTT DATA Technology & Innovation
 
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
論文紹介:Selective Structured State-Spaces for Long-Form Video UnderstandingToru Tamaki
 
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Gamesatsushi061452
 
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...Toru Tamaki
 
新人研修 後半 2024/04/26の勉強会で発表されたものです。
新人研修 後半        2024/04/26の勉強会で発表されたものです。新人研修 後半        2024/04/26の勉強会で発表されたものです。
新人研修 後半 2024/04/26の勉強会で発表されたものです。iPride Co., Ltd.
 
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)Hiroshi Tomioka
 

Dernier (11)

業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
 
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
 
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアルLoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
 
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
LoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイスLoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイス
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
 
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
 
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
 
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
 
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
 
新人研修 後半 2024/04/26の勉強会で発表されたものです。
新人研修 後半        2024/04/26の勉強会で発表されたものです。新人研修 後半        2024/04/26の勉強会で発表されたものです。
新人研修 後半 2024/04/26の勉強会で発表されたものです。
 
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
 

React.jsで広告テンプレートを作りたい #scripty03

  • 1. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 React.jsで 広告テンプレートを 作りたい リッチラボ株式会社 穴井宏幸(@pirosikick) ! SCRIPTY#3 2015/03/10
  • 2. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 React.jsで 広告テンプレートを 作りたい リッチラボ株式会社 穴井宏幸(@pirosikick) ! SCRIPTY#3 2015/03/10
  • 3. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 React.jsで 広告テンプレートを 作りたい リッチラボ株式会社 穴井宏幸(@pirosikick) ! SCRIPTY#3 2015/03/10 リッチ広告
  • 4. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 穴井 宏幸 リッチラボ株式会社 エンジニア @pirosikick (ぴろしきっく) JavaScript, React.js, Flux Golangを始めたい
  • 5. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 話すこと • リッチ広告でReact.jsを使いたい • 検証のためいくつか書きなおした • よかったことや • あんまりよくなかったこと・実戦投入への課題など
  • 6. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止
  • 7. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 /** * "Hello! ${ 入力内容 }"と表示するだけのサンプル */ import React from "react"; ! let HelloApp = React.createClass({ getInitialState () { return { name: this.props.defaultName || '' }; }, ! render () { return ( <div className="wrapper"> <h1>Hello! { this.state.name }</h1> <input type="text" onChange={this.onChange}/> </div> ); }, ! onChange (e) { this.setState({ name: e.target.value }); } }); ! React.render(<HelloApp defaultName="pirosikick" />, document.body);
  • 8. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 • View専門 • Virtual-DOM • JSX(別に使わなくても書けるけども)
  • 9. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 リッチ広告 ちょっとデモ
  • 10. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 リッチ広告 • ユーザイベントで変化させてリッチな感じに • ex) scroll, deviceorientation, touch, etc… • CTRや広告の印象が良かったりする
  • 11. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 なんでReactで 作りたいか • DOMを組み立てていく様が普段の開発と似ていて
 なんか相性良さそう • 単純に新しいことをやりたい
  • 12. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 React.jsで 書き直してみた バナープラス スクロールしている間だけ大きく表示される
  • 13. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 比較
  • 14. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 Before 163行 (独自ライブラリ除く)
  • 15. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 'use strict'; 'use es6'; !import React, { PropTypes } from 'react'; !let Banner = React.createClass({ propTypes: { src: PropTypes.string.isRequired, width: PropTypes.number.isRequired, height: PropTypes.number.isRequired, margin: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) }, !render () { let props = this.props; let style = { width: props.width, height: props.height, margin: props.margin || '0 auto', position: 'relative', background: `transparent url(${props.src}) no-repeat 50% 50%`, backgroundSize: 'contain' }; !return <div style={style}>{props.children}</div>; } }); !let Extension = React.createClass({ propTypes: { src: PropTypes.string.isRequired, width: PropTypes.number.isRequired, height: PropTypes.number.isRequired, offsetTop: PropTypes.number.isRequired, offsetLeft: PropTypes.number.isRequired, shown: PropTypes.bool }, !render () { let props = this.props; let style = { width: props.width, height: props.height, position: 'absolute', top: props.offsetTop, left: props.offsetLeft, zIndex: 100000, opacity: props.shown ? 1 : 0, background: `transparent url(${props.src}) no-repeat 50% 50%`, backgroundSize: 'contain', pointerEvents: 'none', webkitTransition: '-webkit-transform 0s linear', webkitTransform: 'translate3d(0, 0, 0)', webkitTransitionDelay: `${props.show ? 0 : 200}ms` }; !return <div style={style}></div>; } }); !let Anchor = React.createClass({ propTypes: { href: PropTypes.string.isRequired, width: PropTypes.number.isRequired, height: PropTypes.number.isRequired, clickTracking: PropTypes.string }, !render () { let props = this.props; let style = { position: 'absolute', width: props.width, height: props.height, top: 0, left: 0 }; !return <a href={props.href} onClick={this.onClick}></a>; }, !onClick () { if (this.props.clickTracking) { var img = new Image(); img.src = this.props.clickTracking; } } }); !export default React.createClass({ displayName: 'BannerPlus', !propTypes: { param: PropTypes.object.isRequired, option: PropTypes.object }, !getInitialState () { let param = this.props.param; let option = this.props.option || {}; let state = {}; state.link = param.link; state.width = option.width || 320; state.height = option.height || 100; state.banner = { src: param['banner'], margin: option['margin'] || '0 auto' }; state.extension = { src: param['extension'], width: option['extensionWidth'] || 320, height: option['extensionHeight'] || 200, shown: false, }; state.extension.offsetTop = -(state.extension.height - state.height) * 0.5; state.extension.offsetLeft = -(state.extension.width - state.width) * 0.5; state.clickTracking = option['clickTracking']; state.researchTracking = option['researchTracking'] || []; !return state; }, !render () { let state = this.state; !return ( <Banner src={state.banner.src} width={state.width} height={state.height} margin={state.banner.margin}> ! <Anchor href={state.link} width={state.width} height={state.height}></Anchor> ! <Extension src={state.extension.src} width={state.extension.width} height={state.extension.height} offsetTop={state.extension.offsetTop} offsetLeft={state.extension.offsetLeft} shown={state.extension.shown}></Extension> ! <div ref="hoge"></div> </Banner> ); }, !showExtension () { if (!this.state.extension.shown) { this.state.extension.shown = true; this.setState({ extension: this.state.extension }); } }, !hideExtension () { if (this.state.extension.shown) { this.state.extension.shown = false; this.setState({ extension: this.state.extension }); } }, !showExtensionDuringScroll: (function() { let timerId; return function () { this.showExtension(); ! if (timerId) clearInterval(timerId); ! timerId = setInterval(this.hideExtension, 200); } })(), !componentDidMount () { document.body.addEventListener('touchmove', this.showExtensionDuringScroll); window.addEventListener('scroll', this.showExtensionDuringScroll); }, !componentWillUnmount () { document.body.removeEventListener('touchmove', this.showExtensionDuringScroll); window.removeEventListener('scroll', this.showExtensionDuringScroll); } }); After 185行 (本体除く) • ちょっと増えた • 体感的にはそんなには書いていない • propTypesの記述 • JSXを読みやすくするために
 改行を多く入れたのが原因? • 実際はrequireとかで分けるだろう
  • 16. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 イベント処理記述 設定に基づきDOM構築 • ユーザイベントで
 style属性を書き換えるような処理 Before 全体の構成
  • 17. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 /** * DOMの構築 */ function $elem (elemName) { return $(document.createElement(elemName)); } ! // CSSは使えないので直接style属性に定義 var banner $elem('div').css({ 'width': config.width, 'height': config.height, 'margin': config.margin, 'position': 'relative', 'background': `transparent url(${config.banner.src}) no-repeat 50% 50%`, 'backgroundSize': 'contain' }); ! var extension = $elem('div').css(/* 割愛 */); ! var anchor = $elem('a').css(/* 割愛 */); ! anchor .attr('href', config.link) .on('click', function () { ... }; ! banner.append(extension, anchor); Before ※注) 実際のコードは晒せないのでjQueryで似たようなコードを書きました
  • 18. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 import React from "react"; ! let Banner = React.createClass({ propTypes: {/* 割愛 */}, ! render () { let props = this.props; let style = { width: props.width, height: props.height, margin: props.margin || '0 auto', position: 'relative', background: `transparent url(${props.src}) no-repeat 50% 50%`, backgroundSize: 'contain' }; ! return <div style={style}>{props.children}</div>; } }); ! // 上とstyle以外ほとんど一緒なので割愛 let Extension = React.createClass({/* 割愛 */}); let Anchor = React.createClass({/* 割愛 */}); After • そんなに変わらない
  • 19. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 let BannerPlus = React.createClass({ propTypes: { param: ..., option: ... }, ! // this.propsに来た設定値の初期化・Validationなど getInitialState () { let param = this.props.param; let option = this.props.option; return { link: param.link, banner: { ... }, extension: { ... } }; }, ! render () { let state = this.state; return ( // this.stateを子Componentに渡してDOMを構築する <Banner src={state.banner.src} width={...} height={...}> <Anchor href={state.link} width={...} height={...} /> <Extension isShown={state.isExtensionShown} src={...} width={...} height={...} /> </Banner> ); }, ! // 外側の大きいバナーの表示・非表示 showExtension () {/* 後述 */}, hideExtension () {/* 後述 */} }); ! // 広告描画 var param = { ... }, option = { ... } React.render(<BannerPlus param={param} option={option} />, target); After
  • 20. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 ※注) 実際のコードは晒せないのでjQueryで似たようなコードを書きました Before /** * ユーザイベントに合わせてstyle属性の書き換え */ ! var isExtensionShown = false; var timerId; ! // 大きいバナーを表示する処理 function showExtension () { extension.css({ /* 割愛 */ }); isExtensionShown = true; } ! // 多きバナーを隠す処理 function hideExtension () { dom.extension.css({ /* 割愛 */ }); isExtensionShown = false; } ! // スクロール時に表示・非表示 $(window).on('scroll', function () { !isExtensionShown && showExtension(); ! if (timerId) clearInterval(timerId); ! timerId = setInterval(function () { hideExtension(); }, 200); });
  • 21. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 let BannerPlus = React.createClass({ render () { /* 割愛 */ }, ! // state.extension.shownの切り替えで // 子Componentのstyleを切り替える showExtension () { if (!this.state.isExtensionShown) this.setState({ isExtensionShown: true }); }, ! hideExtension () { if (this.state.isExtensionShown) this.setState({ isExtensionShown: false }); }, ! onScroll () { !this.state.isExtensionShown && this.showExtension(); ! if (this._timerId) clearInterval(this._timerId); ! this._timerId = setInterval(function () { this.hideExtension(); }.bind(this), 200); } ! componentDidMount () { window.addEventListener('scroll', this.onScroll); }, ! componentWillUnmount () { window.removeEventListener('scroll', this.onScroll); } }); After
  • 22. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 /** * 外側の大きいバナーのComponent */ ! let Extension = React.createClass({ propTypes: { /* 割愛 */ }, ! render () { let props = this.props; let style = { ! /* 省略 */ ! /** * props.isShownで表示・非表示 */ opacity: props.isShown ? 1 : 0, ! /* 省略 */ ! }; ! return <div style={style}></div>; } }); After
  • 23. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 良い点
  • 24. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 • JSX HTMLを書く感じで心地良い • 全体の見通しがよくなった • Componentに見た目と振る舞いの定 義があるから • 単体テストしやすい • this.props • React.addons.TestUtils
  • 25. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 実戦投入への課題
  • 26. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 うまく 実装できなかったこと • 広告を挿入する要素の外側への処理 • ex) expand時にbodyに要素を付け替え • 実装できてもあんまりいいやり方では無さそう • 2回React.renderを呼び、予めbodyに要素追加 • Reactの外から要素を移動して戻す
  • 27. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 サイズがデカイ • react.min.js v0.12.2 128KB • 大体10KBに収まるように心がけているので、
 めっちゃでかい。。。 • 他のVDOM系もそれなりのサイズ感 • deku.min.js 9.9KB • virtual-dom 28KB(uglifyjs)
  • 28. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 まとめ
  • 29. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 • リッチ広告への実戦投入は
 現状出来なそう🙅 • 容量が小さいものが欲しい • 書いている時の気持ちよさ👏
 (JSXに限る) • コードの見通しがよく感じた👍
  • 30. ©2015 Rich Lab Co., Ltd. All Rights Reserved. 無断利用・転載禁止 Thanks:) @pirosikick (ぴろしきっく)