我曾经有过一次简单的聊天对话,需要以类似于 Messenger 中的设计方式显示它们。由于这是一次长时间的对话,我需要一种方法来一次性完成。我知道这可能无法全部实现,所以我只需要尽可能地模仿它。
- 左对齐灰色气泡(103、184、104),带有黑色文字;
右对齐绿色气泡(241、240、240),带有白色文字 - 气泡仅在一段话之后才会拆分(相当于聊天时按回车键)。多行的长消息将保留在一个气泡中。
- 左右边缘均为半圆
- 相同颜色气泡之间的边距较小,不同颜色气泡之间的边距较大。
供参考:哪些文字处理工具可以处理复杂的文本框?平面设计专业
如何在 Word 中使用自定义文本框包装每个段落?在超级用户中
答案1
和tcolorbox
:
\documentclass{article}
\usepackage[many]{tcolorbox}
\usepackage{xcolor}
\usepackage{varwidth}
\usepackage{environ}
\usepackage{xparse}
\newlength{\bubblewidth}
\AtBeginDocument{\setlength{\bubblewidth}{.75\textwidth}}
\definecolor{bubblegreen}{RGB}{103,184,104}
\definecolor{bubblegray}{RGB}{241,240,240}
\newcommand{\bubble}[4]{%
\tcbox[
colback=#1,
colframe=#1,
#2,
]{\color{#3}\begin{varwidth}{\bubblewidth}#4\end{varwidth}}%
}
\ExplSyntaxOn
\seq_new:N \l__ooker_bubbles_seq
\tl_new:N \l__ooker_bubbles_first_tl
\tl_new:N \l__ooker_bubbles_last_tl
\NewEnviron{rightbubbles}
{
\raggedleft\sffamily
\seq_set_split:NnV \l__ooker_bubbles_seq { \par } \BODY
\int_compare:nTF { \seq_count:N \l__ooker_bubbles_seq < 2 }
{
\bubble{bubblegreen}{rounded~corners}{white}{\BODY}
}
{
\seq_pop_left:NN \l__ooker_bubbles_seq \l__ooker_bubbles_first_tl
\seq_pop_right:NN \l__ooker_bubbles_seq \l__ooker_bubbles_last_tl
\bubble{bubblegreen}{sharp~corners=southeast}{white}{\l__ooker_bubbles_first_tl}\par
\seq_map_inline:Nn \l__ooker_bubbles_seq
{
\bubble{bubblegreen}{sharp~corners=east}{white}{##1}\par
}
\bubble{bubblegreen}{sharp~corners=northeast}{white}{\l__ooker_bubbles_last_tl}\par
}
}
\NewEnviron{leftbubbles}
{
\raggedright\sffamily
\seq_set_split:NnV \l__ooker_bubbles_seq { \par } \BODY
\int_compare:nTF { \seq_count:N \l__ooker_bubbles_seq < 2 }
{
\bubble{bubblegray}{rounded~corners}{black}{\BODY}
}
{
\seq_pop_left:NN \l__ooker_bubbles_seq \l__ooker_bubbles_first_tl
\seq_pop_right:NN \l__ooker_bubbles_seq \l__ooker_bubbles_last_tl
\bubble{bubblegray}{sharp~corners=southwest}{black}{\l__ooker_bubbles_first_tl}\par
\seq_map_inline:Nn \l__ooker_bubbles_seq
{
\bubble{bubblegray}{sharp~corners=west}{black}{##1}\par
}
\bubble{bubblegray}{sharp~corners=northwest}{black}{\l__ooker_bubbles_last_tl}\par
}
}
\ExplSyntaxOff
\begin{document}
\begin{rightbubbles}
Left-aligned gray bubbles (241, 240, 240) with black text
Right-aligned green bubbles (103, 184,104) with white text
Bubbles only break after a paragraph (equivalent to an enter press
when chatting). Long message with multiple lines will be kept in one bubble.
Left and right edges are round.
\end{rightbubbles}
\begin{leftbubbles}
Left-aligned gray bubbles (241, 240, 240) with black text
Right-aligned green bubbles (103, 184,104) with white text
Bubbles only break after a paragraph (equivalent to an enter press
when chatting). Long message with multiple lines will be kept in one bubble.
Left and right edges are round.
\end{leftbubbles}
\begin{rightbubbles}
Single
\end{rightbubbles}
\begin{leftbubbles}
End
\end{leftbubbles}
\end{document}
\bubble
如果我把的定义改为
\newcommand{\bubble}[4]{%
\tcbox[
arc=5mm,
colback=#1,
colframe=#1,
#2,
]{\color{#3}\begin{varwidth}{\bubblewidth}#4\end{varwidth}}%
}
输出如下:
改良版
您可以通过设置 来定义气泡之间的间距\bubblesep
。不同颜色的气泡被封闭在flushleft
和flushright
环境中,因此它们之间会有\topsep
间隙。
\documentclass{article}
\usepackage[many]{tcolorbox}
\usepackage{xcolor}
\usepackage{varwidth}
\usepackage{environ}
\usepackage{xparse}
\newlength{\bubblesep}
\newlength{\bubblewidth}
\setlength{\bubblesep}{2pt}
\AtBeginDocument{\setlength{\bubblewidth}{.75\textwidth}}
\definecolor{bubblegreen}{RGB}{103,184,104}
\definecolor{bubblegray}{RGB}{241,240,240}
\newcommand{\bubble}[4]{%
\tcbox[
on line,
arc=4.5mm,
colback=#1,
colframe=#1,
#2,
]{\color{#3}\begin{varwidth}{\bubblewidth}#4\end{varwidth}}%
}
\ExplSyntaxOn
\seq_new:N \l__ooker_bubbles_seq
\tl_new:N \l__ooker_bubbles_first_tl
\tl_new:N \l__ooker_bubbles_last_tl
\NewEnviron{rightbubbles}
{
\begin{flushright}
\sffamily
\seq_set_split:NnV \l__ooker_bubbles_seq { \par } \BODY
\int_compare:nTF { \seq_count:N \l__ooker_bubbles_seq < 2 }
{
\bubble{bubblegreen}{rounded~corners}{white}{\BODY}\par
}
{
\seq_pop_left:NN \l__ooker_bubbles_seq \l__ooker_bubbles_first_tl
\seq_pop_right:NN \l__ooker_bubbles_seq \l__ooker_bubbles_last_tl
\bubble{bubblegreen}{sharp~corners=southeast}{white}{\l__ooker_bubbles_first_tl}
\par\nointerlineskip
\addvspace{\bubblesep}
\seq_map_inline:Nn \l__ooker_bubbles_seq
{
\bubble{bubblegreen}{sharp~corners=east}{white}{##1}
\par\nointerlineskip
\addvspace{\bubblesep}
}
\bubble{bubblegreen}{sharp~corners=northeast}{white}{\l__ooker_bubbles_last_tl}
\par
}
\end{flushright}
}
\NewEnviron{leftbubbles}
{
\begin{flushleft}
\sffamily
\seq_set_split:NnV \l__ooker_bubbles_seq { \par } \BODY
\int_compare:nTF { \seq_count:N \l__ooker_bubbles_seq < 2 }
{
\bubble{bubblegray}{rounded~corners}{black}{\BODY}\par
}
{
\seq_pop_left:NN \l__ooker_bubbles_seq \l__ooker_bubbles_first_tl
\seq_pop_right:NN \l__ooker_bubbles_seq \l__ooker_bubbles_last_tl
\bubble{bubblegray}{sharp~corners=southwest}{black}{\l__ooker_bubbles_first_tl}
\par\nointerlineskip
\addvspace{\bubblesep}
\seq_map_inline:Nn \l__ooker_bubbles_seq
{
\bubble{bubblegray}{sharp~corners=west}{black}{##1}
\par\nointerlineskip
\addvspace{\bubblesep}
}
\bubble{bubblegray}{sharp~corners=northwest}{black}{\l__ooker_bubbles_last_tl}\par
}
\end{flushleft}
}
\ExplSyntaxOff
\begin{document}
\begin{rightbubbles}
Left-aligned gray bubbles (241, 240, 240) with black text
Right-aligned green bubbles (103, 184,104) with white text
Bubbles only break after a paragraph (equivalent to an enter press
when chatting). Long message with multiple lines will be kept in one bubble.
Left and right edges are round.
\end{rightbubbles}
\begin{leftbubbles}
Left-aligned gray bubbles (241, 240, 240) with black text
Right-aligned green bubbles (103, 184,104) with white text
Bubbles only break after a paragraph (equivalent to an enter press
when chatting). Long message with multiple lines will be kept in one bubble.
Left and right edges are round.
\end{leftbubbles}
\begin{rightbubbles}
Single
\end{rightbubbles}
\begin{leftbubbles}
End
\end{leftbubbles}
\end{document}
答案2
在纯 TeX(使用 pdfTeX)中我们可以这样做:
\font\ssf=cmssdc10
\def\Black{\pdfliteral{0 g}}
\def\Blue{\pdfliteral{0 0 1 rg}}
\def\White{\pdfliteral{1 g}}
\def\bptopt#1{.9963 0 0 .9963 0 0 cm 17 0 0 #117 0 3.5 cm }
\def\circle{.5 0 m .5 .276 .276 .5 0 .5 c -.276 .5 -.5 .276 -.5 0 c
-.5 -.276 -.276 -.5 0 -.5 c .276 -.5 .5 -.276 .5 0 c }
\def\hcircle{.5 .276 .276 .5 0 .5 c -.276 .5 -.5 .276 -.5 0 c }
\def\fullround{\pdfliteral{q \bptopt+ \circle f Q}}
\def\halfround{%
\ifnum\count255=13
\pdfliteral{q \bptopt+ -.5 -.5 m .5 -.5 l .5 0 l \hcircle h f Q}%
\else \ifnum\count255=14
\pdfliteral{q \bptopt- -.5 -.5 m .5 -.5 l .5 0 l \hcircle h f Q}%
\else
\pdfliteral{q \bptopt+ -.5 -.5 m -.5 .5 l .5 .5 l .5 -.5 l h f Q}%
\fi\fi
}
\def\xround#1#2{\if #1#2\halfround \else \fullround\fi}
\def\messenger#1#2{\medskip\setbox0=\vbox{\penalty13
\widowpenalty=14 \clubpenalty=0 \interlinepenalty=0
\def\\{\unskip\break}\rightskip=0pt plus 1fil\parindent=0pt \ssf #2\par
\setbox1=\box2
\loop \setbox1=\lastbox
\unless\ifvoid1
\unskip\unskip \count255=\lastpenalty \unpenalty
\setbox1=\hbox{\unhbox1\unskip\unskip}
\setbox1=\hbox to\hsize{\hss\Blue\xround l#1%
\rlap{\vrule height12pt depth5pt width\wd1 \xround r#1}\White
\box1 \Black \if l#1\hfill\fi}
\global\setbox2=\vbox{\box1 \vskip1pt \unvbox2}
\repeat
}
\unvbox2
\medskip
}
\hsize=9cm
This is test:
\messenger r {
This is the native format from messenger \\
Notice, how the borders of the sides are circular, not straight and the last
notice is here.
}
\messenger l {
Second message \\
is here.
}
\bye
编辑:我修改了代码,以便能够打印两种变体:左对齐(如果\messenger l
使用)和右对齐(如果\messenger r
使用)。正如您的评论所述。
结果:
编辑在我的第一个结果发布在这里之后,OP 的规范发生了变化。首先,颜色为蓝色,气泡右对齐。其次,气泡也必须左对齐。规范的最后一个变化包括对颜色和段落格式的新要求。我认为当我的代码发布后,对段落格式或颜色进行细微更改对 OP 来说只是简单的任务。但可能无法指望,因为 OP 不知道这是什么意思\bye
。此外,egreg 在这里基于特定的 Expl3 语言提出了第二种解决方案。他有优势,因为他知道 OP 的最新规范。我不能只保留 Expl3 解决方案,因为在我看来,它是晦涩难懂的语言,无法帮助教授基本的 TeX 原理。所以,我有了第二个解决方案,使用 TeX 原语并接受 OP 的新规范。我希望 OP 不会再有规范更改。
\font\ssf=cmssdc10
\def\Black{\pdfliteral{0 g}}
\def\Blue{\pdfliteral{0 0 1 rg}}
\def\White{\pdfliteral{1 g}}
\def\Gray{\pdfliteral{.945 .941 .941 rg}}
\def\Green{\pdfliteral{.404 .722 .408 rg}}
\def\bcolor#1{\if r#1\Green \else \Gray \fi}
\def\fcolor#1{\if r#1\White \else \Black \fi}
\def\bptopt#1{.9963 0 0 .9963 0 0 cm 17 0 0 #117 0 3.5 cm }
\def\circle{.5 0 m .5 .276 .276 .5 0 .5 c -.276 .5 -.5 .276 -.5 0 c
-.5 -.276 -.276 -.5 0 -.5 c .276 -.5 .5 -.276 .5 0 c }
\def\hcircle{.5 .276 .276 .5 0 .5 c -.276 .5 -.5 .276 -.5 0 c }
\def\fullround{\pdfliteral{q \bptopt+ \circle f Q}}
\def\halfround{%
\ifcase\count255
\pdfliteral{q \bptopt+ -.5 -.5 m -.5 .5 l .5 .5 l .5 -.5 l h f Q}%
\or \pdfliteral{q \bptopt+ -.5 -.5 m .5 -.5 l .5 0 l \hcircle h f Q}%
\or \pdfliteral{q \bptopt- -.5 -.5 m .5 -.5 l .5 0 l \hcircle h f Q}%
\or \fullround \fi
}
\def\xround#1#2{\ifdim\ht1>12pt
\dimen0=\ht1 \advance\dimen0 by-12pt
\hbox to0pt{\hss\vbox{\xroundA#1#2\kern\dimen0 \xroundA#1#2}%
\rlap{\kern-8.5pt\raise4.5pt\vbox{\hrule width17pt height\dimen0}}\hss}%
\else \xroundA#1#2\fi}
\def\xroundA#1#2{\if #1#2\halfround \else \fullround\fi}
\long\def\messenger#1#2{\medskip\setbox0=\vbox{\penalty13
\widowpenalty=0 \clubpenalty=0 \interlinepenalty=0
\everypar={\setbox0=\lastbox\endgraf\vbox\bgroup
\everypar={\vrule height12pt width0pt}}
\def\par{\ifhmode\endgraf\egroup\fi}
\def\\{\unskip\break}\rightskip=0pt plus 1fil\parindent=0pt \ssf #2\par
\everypar={}\let\par=\endgraf \setbox1=\box2
\loop \setbox1=\lastbox
\unless\ifvoid1
\unskip\unskip
\count255=\ifvoid2 \ifnum\lastpenalty=13 3\else 2\fi
\else \ifnum\lastpenalty=13 1\else 0\fi \fi
\unpenalty
\ifdim\ht1>12pt \else
\setbox0=\vbox{\unvbox1 \lastbox \setbox1=\lastbox
\global\setbox1=\hbox{\unhbox1\unskip\unskip}}
\fi
\setbox1=\hbox to\hsize{\hss\bcolor#1\xround l#1%
\rlap{\vrule height\ht1 depth5pt width\wd1 \xround r#1}%
\fcolor#1\box1 \Black \if l#1\hfill\fi}
\global\setbox2=\vbox{\box1 \vskip2pt \unvbox2}
\repeat
}
\unvbox2
\medskip
}
\hsize=9cm
This is test:
\messenger r {
Left-aligned gray bubbles (241, 240, 240) with black text
Right-aligned green bubbles (103, 184,104) with white text
Bubbles only break after a paragraph (equivalent to an enter press
when chatting). Long message with multiple lines will be kept in one bubble.
Left and right edges are round.
}
\messenger l {
Left-aligned gray bubbles (241, 240, 240) with black text
Right-aligned green bubbles (103, 184,104) with white text
Bubbles only break after a paragraph (equivalent to an enter press
when chatting). Long message with multiple lines will be kept in one bubble.
}
\messenger r {
Single
}
\messenger l {
End
}
\bye