CSSでメッセージアプリ風のUIを再現してみました。
是非ともiPhoneでご観覧頂きたいです。
HTML
<div id="global-wrapper"> <header id="global-header"> <h1 class="ttl">Message</h1> </header> <div id="global-stage"> <div id="global-stage-inner" class="inner"> </div> </div> <footer id="global-footer"> <form id="global-footer-form" class="form"> <input id="global-footer-form-txt" class="txt" type="text"></input> <input id="global-footer-form-btn" class="btn submit" type="submit" value="送信" /> </form> </footer> </div>
CSS
@charset "UTF-8"; section, nav, article, aside, hgroup, header, footer, figure, figcaption, details, summary, main { display: block; } output { display: inline; } progress, meter { display: inline-block; } datalist { display: none; } html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font: inherit; font-size: 100%; vertical-align: baseline; } html { line-height: 1; } ol, ul { list-style: none; } table { border-collapse: collapse; border-spacing: 0; } caption, th, td { text-align: left; font-weight: normal; vertical-align: middle; } q, blockquote { quotes: none; } q:before, q:after, blockquote:before, blockquote:after { content: ""; content: none; } a img { border: none; } article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary { display: block; } html { height: 100%; } body { height: 100%; font: 16px "Hiragino Kaku Gothic Pro", "ヒラギノ角ゴ Pro W3", Meiryo, "メイリオ", sans-serif; } #global-wrapper { position: relative; width: 100%; height: 100%; } #global-header { position: relative; width: 100%; height: 44px; text-align: center; background: -webkit-linear-gradient(#3e4758, #141821); background: linear-gradient(#3e4758, #141821); box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); z-index: 10; } #global-header .ttl { color: #fff; font-weight: bold; letter-spacing: .2em; line-height: 44px; } #global-stage { position: absolute; top: 44px; bottom: 44px; left: 0; right: 0; background: #e4eef7; overflow: scroll; -webkit-overflow-scrolling: touch; } #global-footer { position: absolute; bottom: 0; width: 100%; height: 44px; background: -webkit-linear-gradient(#e4ebf1, #b1b5b8); background: linear-gradient(#e4ebf1, #b1b5b8); box-shadow: 0 -2px 2px rgba(0, 0, 0, 0.2); overflow: hidden; z-index: 10; } #global-footer .form { position: relative; margin: 0 auto; width: 320px; } #global-footer .txt { position: absolute; border-color: rgba(0, 0, 0, 0.2); top: 6px; left: 10px; border-radius: 9px; width: 200px; height: 24px; box-shadow: 0 0 2px rgba(0, 0, 0, 0.2) inset; -webkit-appearance: none; } #global-footer .btn { position: absolute; padding: 0; border-color: rgba(0, 0, 0, 0.2); border-radius: 10px; top: 7px; right: 10px; width: 50px; height: 30px; color: #fff; font-size: 16px; font-weight: bold; text-align: center; background: -webkit-linear-gradient(#4f94dd, #2a5da1); background: linear-gradient(#4f94dd, #2a5da1); -webkit-appearance: none; } .msg { -webkit-transition: opacity .5s ease; transition: opacity .5s ease; } .msg .txt { display: inline-block; position: relative; margin: 20px; padding: 5px 10px; border-radius: 15px; background: -webkit-linear-gradient(#ffffff, #dbdfe2); background: linear-gradient(#ffffff, #dbdfe2); box-shadow: 0 0 2px rgba(0, 0, 0, 0.8); } .msg .txt:after { display: block; position: absolute; margin-top: -6px; top: 50%; left: -16px; border: solid 6px rgba(0, 0, 0, 0); border-right: solid 10px #b9b9b9; width: 0; height: 0; content: ""; } .msg.mine { text-align: right; } .msg.mine .txt { background: -webkit-linear-gradient(#c7f377, #97d267); background: linear-gradient(#c7f377, #97d267); } .msg.mine .txt:after { top: 50%; left: auto; right: -16px; border: solid 6px rgba(0, 0, 0, 0); border-left: solid 10px #b9b9b9; width: 0; height: 0; content: ""; } .msg.invisble { opacity: 0; }
JavaScript
(function(win, doc) { "use strict"; win.App = win.App || {}; })(this, document); (function(win, doc, ns) { "use strict"; function EventDispatcher() { this._events = {}; } EventDispatcher.prototype.hasEventListener = function(eventName) { return !!this._events[eventName]; }; EventDispatcher.prototype.addEventListener = function(eventName, callback) { if (this.hasEventListener(eventName)) { var events = this._events[eventName], length = events.length, i = 0; for (; i < length; i++) { if (events[i] === callback) { return; } } events.push(callback); } else { this._events[eventName] = [callback]; } }; EventDispatcher.prototype.removeEventListener = function(eventName, callback) { if (!this.hasEventListener(eventName)) { return; } else { var events = this._events[eventName], i = events.length, index; while (i--) { if (events[i] === callback) { index = i; } } events.splice(index, 1); } }; EventDispatcher.prototype.fireEvent = function(eventName, opt_this, opt_arg) { if (!this.hasEventListener(eventName)) { return; } else { var events = this._events[eventName], copyEvents = _copyArray(events), arg = _copyArray(arguments), length = events.length, i = 0; // eventNameとopt_thisを削除 arg.splice(0, 2); for (; i < length; i++) { copyEvents[i].apply(opt_this || this, arg); } } function _copyArray(array) { var newArray = [], i = 0; try { newArray = [].slice.call(array); } catch(e) { for (; i < array.length; i++) { newArray.push(array[i]); } } return newArray; } }; ns.EventDispatcher = EventDispatcher; })(this, document, App); (function(win, doc, $, ns) { "use strict"; var instance, originalConstructor; function MassageList() { var that = this; _init(); function _init() { ns.EventDispatcher.call(that); } this.him = [ "hello world." ]; } originalConstructor = MassageList.prototype.constructor; MassageList.prototype = new ns.EventDispatcher(); MassageList.prototype.constructor = originalConstructor; originalConstructor = null; MassageList.getInstance = function() { if (!instance) { instance = new MassageList(); } return instance; }; ns.MassageList = MassageList; })(this, document, $, App); (function(win, doc, $, ns) { "use strict"; var instance, originalConstructor; function MassageManager() { var massageList = ns.MassageList.getInstance(); var that = this, historyList = [], index = 0, LOOP_INDEX = 1; _init(); function _init() { ns.EventDispatcher.call(that); receive(); } function _handlePost(evt) { var interval = 500 + Math.random() * 1000 | 0; that.fireEvent("POST", evt, evt); if (evt.target === "mine") { setTimeout(function() { receive(); }, interval); } } function send(txt) { if (!txt) { return; } var msg = new ns.Massage(txt, true); msg.addEventListener("POST", _handlePost); historyList.push(msg); } function receive() { var msg = new ns.Massage(massageList.him[index], false); msg.addEventListener("POST", _handlePost); historyList.push(msg); if (!!massageList.him[index + 1]) { ++index; } else { index = massageList.him.length - LOOP_INDEX; } } this.send = send; } originalConstructor = MassageManager.prototype.constructor; MassageManager.prototype = new ns.EventDispatcher(); MassageManager.prototype.constructor = originalConstructor; originalConstructor = null; MassageManager.getInstance = function() { if (!instance) { instance = new MassageManager(); } return instance; }; ns.MassageManager = MassageManager; })(this, document, $, App); (function(win, doc, $, ns) { "use strict"; var $stage = $("#global-stage"), $inner = $stage.find("#global-stage-inner"), originalConstructor; function Massage(txt, isMine) { if (!txt) { return; } var that = this, klass = isMine ? "invisble msg mine" : "invisble msg", $msg = $('<div class="' + klass + '"><p class="txt">' + txt + '</p></div>'); _init(); function _init() { ns.EventDispatcher.call(that); $inner.append($msg); $stage.animate({scrollTop: $inner.height()}, 200); setTimeout(function() { $msg.removeClass("invisble"); that.fireEvent("POST", that, that); }, 100); } that.txt = txt; that.target = isMine ? "mine" : "him"; } originalConstructor = Massage.prototype.constructor; Massage.prototype = new ns.EventDispatcher(); Massage.prototype.constructor = originalConstructor; originalConstructor = null; ns.Massage = Massage; })(this, document, $, App); (function(win, doc, $, ns) { "use strict"; var messageManager = ns.MassageManager.getInstance(), $form = $("#global-footer-form"), $txt = $("#global-footer-form-txt"); $form.on("submit", handleSubmit); function handleSubmit() { messageManager.send($txt.val(), true); $txt.val(""); return false; } })(this, document, $, App);
シャドウとかグラデーションがつかえるようになるとCSSが楽しくなりますね。
はやく対応してほしいものです。