声劇台本を投稿できるサイトを作成しました。
ボイコネというSNSアプリのフォーマットの台本をそのまま投稿できるようにしています。
ボイコネユーザーの脚本家の方は、編集の手間なしに以下Webサイトに台本を投稿し、Webで公開することができます。
ただ、Web上にある台本サイトはたいてい横書きなのですが、横書きだと非常にセリフが読みづらい・・・。
そのため、縦書きで台本を表示できるかを試してみました。
環境
・PHP7.4 (フレームワーク:fuelPHP)
・MariaDB
・jQuery3.3
・Bootstrap4
やりたいこと
・DBに格納された台本テキストを横書きHTMLで描画
・「縦書きで表示」ボタンを押すとモーダルで縦書き画面を表示
DBに格納された台本テキスト原文
ブラウザ上での表示(横書き)
ブラウザ上での表示(縦書き)
処理内容説明
- DBに格納された台本テキストをPHPで描画
- 上記テキストをjQueryでテーブル形式の横書き台本として描画
- 上記テキストをjQueryで縦書き台本として描画。非表示に。
- 「縦書きに切り替え」ボタンを押したときのみ、モーダルで縦書きテキストを表示
コード (一部のみ抜粋)
detail.php
<!-- 縦書き台本描画欄(モーダル) -->
<div id = "vertical_modal_container" class = "d-none">
<div id = "vertical_modal_body">
<div id = "vertical_button_area" class = "clearfix">
<div class = "float-right">
<input type = "button" id = "close_vertical_button" class = "btn btn-outline-secondary" value = "× 縦書き画面を閉じる"></input>
</div>
</div>
<div id = "vertical_script_area" class = "">
<!-- 台本のテキストを描画 -->
<?php echo $script_body; ?>
</div>
</div>
</div>
<!-- 横書き台本描画欄 -->
<div id = "horizon">
<!-- 縦書きに切り替えボタン -->
<div id = "show_by_vertical_button_area" class = "clearfix">
<div class = "float-right">
<input type = "button" id = "show_by_vertical_button" class = "btn btn-outline-secondary" value = "縦書きで表示"></input>
</div>
</div>
<!-- 台本のテキストを描画(非表示)-->
<div id = "script_write_area" class = "d-none">
<?php echo $script_body; ?>
</div>
<!-- セリフ描画欄(テーブル)-->
<div id = "script_show_area">
</div>
</div>
style.css
/*======================================================
/* 台本描画欄(縦書き)
//=====================================================*/
/* モーダル背景 */
#vertical_modal_container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 36px 8px;
overflow: auto;
background: rgba(0,0,0,60%);
opacity: 0;
transition: .3s;
visibility: hidden;
box-sizing: border-box;
}
/* モーダル背景 アクティブ時 */
#vertical_modal_container.active{
opacity: 1;
visibility: visible;
}
/* モーダル本体 */
div#vertical_modal_body {
width: 100%;
max-width: 1400px;
margin: 0 auto;
height: 95%;
}
/* 閉じるボタンの枠 */
div#vertical_button_area {
width: 100%;
padding: 8px 8px;
text-align: right;
font-size: 100%;
background-color: #EDE3CB;
border: solid 1px #666666;
}
/* 閉じるボタン */
div#vertical_button_area input {
font-size: 90%;
background-color: #ffffff;
}
div#vertical_button_area input:hover {
font-size: 90%;
background-color: #666666;
}
/* 台本描画欄 */
div#vertical_script_area {
width: 100%;
height: 100% auto;
-ms-writing-mode: tb-rl;
writing-mode: vertical-rl;
overflow: scroll;
padding: 16px 36px;
background-color: #ffffff;
border-right: solid 1px #333333;
border-left: solid 1px #333333;
border-bottom: solid 1px #333333;
}
/* 台本描画欄 キャラ名 + セリフ */
div#vertical_script_area div {
padding: 4px;
background-color: #ffffff;
cursor: pointer;
}
/* キャラ名 */
div#vertical_script_area span {
display: block;
min-height: 80px;
height: fit-content;
padding: 8px 0px;
border-radius: 0px;
}
/* キャラ名の色分け */
span.ffffff {
color: #ffffff;
background: #ffffff;
}
span.fbf8c4 {
background: #fbf8c4;
border: solid 1px #bcbb84;
}
span.ffe0e0 {
background: #ffe0e0;
border: solid 1px #d6b8b8;
}
span.e2f2fe {
background: #e2f2fe;
border: solid 1px #a2bbcd;
}
span.edf9d7 {
background: #edf9d7;
border: solid 1px #a2bd6a;
}
span.f7eed0 {
background: #f7eed0;
border: solid 1px #c2aa5c;
}
/* セリフ */
div#vertical_script_area p {
margin: 12px 12px;
font-size: 120%;
cursor: pointer;
}
/* 縦書き第本描画欄 モバイル */
@media only screen and (max-width: 750px) {
div#vertical_button_area {
font-size: 80%;
}
div#vertical h2 {
font-size: 120%;
margin-left: 30px;
}
div#vertical_script_area span {
font-size: 90%;
padding: 6px 4px 6px 2px;
}
div#vertical_script_area p {
margin: 8px 8px;
font-size: 105%; /*110%*/
}
}
/* 縦書き台本描画欄 モバイル横向き */
@media (orientation: landscape) {
/* モーダル背景 */
#vertical_modal_container {
padding: 0px 10px;
}
/* ボタンエリア */
div#vertical_button_area {
width: 100%;
padding: 4px 4px;
text-align: right;
font-size: 100%;
background-color: #EDE3CB;
border: solid 1px #666666;
}
div#vertical_button_area input {
font-size: 80%;
background-color: #ffffff;
}
div#vertical_script_area p {
margin: 8px 8px;
font-size: 100%;
}
}
common.js
/*======================================================
/*
/* 3-1. 台本詳細ページ イベント 縦書きに切り替えボタンクリック
/*
/*======================================================*/
/**
* 台本詳細ページ イベント 縦書きに切り替えボタンクリック
*/
$("#show_by_vertical_button").click(function() {
$("#vertical_modal_container").removeClass("d-none")
$("#vertical_modal_container").show();
$("#vertical_modal_container").addClass('active');
$("#horizon").hide();
});
/*======================================================
/*
/* 3-2. 台本詳細ページ イベント 縦書き画面を閉じるボタンクリック
/*
/*======================================================*/
/**
* 台本詳細ページ イベント 縦書きに切り替えボタンクリック
*/
$("#close_vertical_button").click(function() {
$("#vertical_modal_container").removeClass('active');
$("#horizon").show();
});
/*======================================================
/*
/* 3-3. 台本詳細ページ セリフをテーブル形式で描画(横書き)
/*
/*======================================================*/
/**
* セリフをテーブル形式で描画(横書き)
*/
function show_txt_by_table() {
var NARRE_TXT_PREFIX = "――― "
var table_tag = "<table class = 'script'><tr><th></th><th> </th></tr>"
// 台本のテキストを改行で分割して取得
var line_txts = get_line_txts_from_div($("#script_write_area"));
// テキストから名前だけをセット
var names = get_names(line_txts)
// 行を走査
for ( i = 0; i < line_txts.length; i++ ) {
// 該当行のテキストをセット
var ltxt = line_txts[i];
// 名前をセット
var txts = ltxt.split(":");
var name = txts[0]
// セリフをセット
var vc = txts[1]
// 前の行の氏名をセット
var p_name = get_line_name(line_txts, i - 1)
// 前の行の氏名と異なれば新しい行へ
if ( p_name != name ) {
// 1行目以外は閉じタグを追加
if ( i != 0 ) {
table_tag += "</td></tr>"
}
var ncl = get_name_class(names, name);
table_tag += "<tr class = '" + ncl + "'><td class = 'name'><span class = '" + ncl + "'>" + name + "</td>" + "<td class = 'text'>"
// ト書きの場合のみ、--を追加
if (name == "0") {
vc = NARRE_TXT_PREFIX + vc
}
// セリフを描画
table_tag += vc;
// 前の行と氏名が同じなら改行+セリフのみ
} else {
table_tag += "<br />" + vc
}
}
table_tag += "</tr></table>"
// 描画
$("#script_show_area").html(table_tag);
}
//======================================================
// divタグから ":" 付の行のテキストのみを配列で返す
//======================================================
/**
* divタグから:付の行のテキストのみを配列で返す
*/
function get_line_txts_from_div(div_block) {
var line_txts = []
// 行で分割
var splits = div_block.text().split(/\r\n|\n/);
for (i = 0; i < splits.length; i++) {
var line_txt = splits[i].trim();
// :を含めば
if (line_txt.indexOf(":") != - 1) {
line_txts.push(line_txt)
}
}
return line_txts;
}
//======================================================
// 台本の該当行のキャラ名を返す
//======================================================
/**
* 台本の該当行のキャラ名を返す
* ・例:"キャラ1:セリフ" → "キャラ1"
*
* @param line_txts 第本テキスト
* @param i 行番号
* @return キャラ名
*/
function get_line_name(line_txts, i) {
// 0行目未満ならスキップ
if ( i < 0 ) {
return ""
}
// 該当行のテキストをセット
var ltxt = line_txts[i]
// ":"で分割
var txts = ltxt.split(":");
return txts[0]
}
//======================================================
// キャラ名だけを配列で返す
//======================================================
/**
* キャラ名だけを配列で返す
*/
function get_names(line_txts) {
var names = []
// 台本テキストを走査
for ( i = 0; i < line_txts.length; i++ ) {
// 該当行のテキストをセット
var ltxt = line_txts[i];
// ":"で分割して名前をセット
var txts = ltxt.split(":");
var name = txts[0]
if ( names.indexOf(name) == -1 ) {
names.push(name)
}
}
return names
}
//======================================================
// 該当のキャラ名のspanタグ用クラスを返す
//======================================================
/**
* 該当のキャラ名のspanタグ用クラスを返す
* ・"0"の場合は"ffffff"を返す
* ・キャラクター紹介用テーブルから該当キャラのクラスを返す
*
* @return cl_txt
*/
function get_name_class(names, name) {
var txt = "";
const NARRATION_CLASS = "ffffff"
// 0の場合は"ffffff"を返す
if (name == "0") {
return NARRATION_CLASS;
}
// キャラテーブルを走査
$('table#chara tr').each(function(i) {
// nameセルをセット
var name_td = $(this).find("td.name");
var span_td = $(this).find("span");
// 名前が一致すれば
if (span_td.text() == name) {
var cl = span_td.attr("class");
cl_txt = cl;
return;
} else if(name.indexOf(span_td.text()) !== -1) {
var cl = span_td.attr("class");
cl_txt = cl;
}
});
return cl_txt;
}
/*======================================================
/*
/* 3-4. 台本詳細ページ セリフを描画(縦書き)
/*
/*======================================================*/
/**
* セリフを縦書き形式で描画
*/
function show_txt_by_vertical() {
var NARRE_TXT_PREFIX = "――― "
var all_tag = "";
// 行で分割
var sc_div = $("#vertical_script_area")
var line_txts = get_line_txts_from_div(sc_div);
// タイトルをセット
var title = $('td.title').text();
// 名前だけをセット
var names = get_names(line_txts)
// 行を走査
for ( i = 0; i < line_txts.length; i++ ) {
// 該当行のテキストをセット
var ltxt = line_txts[i];
// 名前をセット
var txts = ltxt.split(":");
var name = txts[0]
// セリフをセット
var vc = txts[1]
// 前の行の氏名をセット
var p_name = get_line_name(line_txts, i - 1)
// 前の行の氏名と異なれば新しい行へ
if ( p_name != name ) {
// 1行目以外は閉じタグを追加
if ( i != 0 ) {
all_tag += "</p></div>"
}
var ncl = get_name_class(names, name);
all_tag += "<div class = '" + ncl + "'>"
all_tag += "<span class = '" + ncl + "'>" + name + "</span>" + "<p class = '" + ncl + "'>"
// ト書きの場合のみ、--を追加
if (name == "0" || name == "") {
vc = NARRE_TXT_PREFIX + vc
}
// セリフを描画
all_tag += vc;
// 前の行と氏名が同じなら改行+セリフのみ
} else {
all_tag += "<br />" + vc
}
}
all_tag += "</p></div>"
// 描画
$("#vertical_script_area").html(all_tag);
}
/*======================================================
/*
/* 3-5. 台本詳細ページ イベント 縦書きセリフクリック
/*
/*======================================================*/
/**
* 縦書きセリフクリック
*/
$("div#vertical_script_area div").click(function() {
// 段落のクラスを取得
var cl = $(this).attr("class");
var bg = rgbTo16($(this).css("background-color"));
// 縦書き台本の段落を走査
$('div#vertical_script_area div').each(function(i) {
// nameセルをセット
var tcl = $(this).attr("class");
// クラスが一致すれば
if (tcl == cl) {
if (bg == "#ffffff") {
$(this).css("background-color", "#efefef");
} else {
$(this).css("background-color", "#ffffff");
}
}
});
});
});
意外といけますね。
縦書きHTMLは普段全く使わないのでかなり新鮮でした。