连接气球

连接气球

我正在尝试制作漫画格式的海报。除了tikZ使用 beamerposter 效果不佳之外,我的想法是制作一种带有相互连接的气球的漫画。

请注意,我不想要通常可以轻松重现的单线连接节点tikZ。这里的连接是双线。从根本上讲,我可以绘制这样的形状,但我真的无法在单个方块中书写。有什么帮助或提示吗?在此处输入图片描述

我使用的代码非常简单

\documentclass[final,hyperref={pdfpagelabels=false}]{beamer}

\usepackage[english]{babel}

\usepackage[orientation=portrait,size=a0,scale=1.4,debug]{beamerposter}

\usepackage{tikz}

\newlength{\columnheight}

\setlength{\columnheight}{105cm}

\begin{document}

\begin{tikzpicture}

 \draw[line width=0.5ex,rounded corners = 1ex, fill=white, font={peppe}] (0,0) --(0,14)--(20,14)--(20,6.9)--(30,6.9)--(30,14)--(50,14)--(50,0)--(30,0)--(30,6.3)--(20,6.3)--(20,0)--(0,0); 

\end{tikzpicture}
\end{document}

答案1

实现此目的的一些技巧和想法:

例子

  1. 节点的形状是“超椭圆”,而不是矩形,看起来更像“漫画”(见问题如何在 tikz 中制作超椭圆节点形状?
  2. 气球之间的连接不是双线,而是非常粗的黑线,顶部有一条非常粗(但较细)的白线。如果背景也是白色,这种方法就很管用。
  3. 最后的标注也很棘手。一个辅助矩形小白色节点用于“擦除”最后一个气球的底部,并用作命名坐标来绘制曲线标注。

代码:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,positioning}
\newcommand*{\superellipse}[3][draw]{% #1 = styles
                                     % #2 = node
                                     % #3 = superness
    \coordinate (ne) at ($(#2.center)!#3!(#2.north east)$);
    \coordinate (nw) at ($(#2.center)!#3!(#2.north west)$);
    \coordinate (sw) at ($(#2.center)!#3!(#2.south west)$);
    \coordinate (se) at ($(#2.center)!#3!(#2.south east)$);
    \pgfmathparse{-9.6*#3*#3 + 14.4*#3 - 4.8}   % <--------- Magic numbers!
    \xdef\tension{\pgfmathresult}
    \path[#1]
       plot[smooth cycle, tension=\tension] coordinates{ 
         (#2.east) (ne) (#2.north) (nw) (#2.west) (sw) (#2.south) (se)};
%    \node {\tension};
}

\tikzset{
  connection/.style = {line width=2mm, white, shorten >=-3pt, shorten <=-3pt,
     preaction={draw, black, line width=3mm, shorten >=0pt, shorten <=0pt}, 
  }
}

\begin{document}
\begin{tikzpicture}
\node[text width=7em, inner sep=1.5em] 
(balloon1) {Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
            sed do eiusmod tempor};
\node[text width=12em, inner sep=1.5em, right=of balloon1.north east] 
(balloon2) {incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,};

\node[text width=11em, inner sep=1.5em, below=of balloon2] 
(balloon3) {quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat};
\foreach \n in {1,2,3}
  \superellipse[draw, very thick]{balloon\n}{0.85};

\draw[connection] ($(balloon1.north east)+(-2ex,-2ex)$) to [out=30, in=180] (balloon2.west);
\draw[connection] (balloon2.south) -- (balloon3.north);

\node[fill=white, minimum width=3mm, minimum height=1mm, inner sep=0] at (balloon3.south) (callout) {};
\draw[very thick] (callout.west) to[bend left] +(-5mm,-1cm) to[bend right] (callout.east);

\end{tikzpicture}
\end{document}

更新。

一些改进:

  1. 我现在不再\superellipse使用宏,而是使用Jake 的回答针对这个问题如何在 tikz 中制作超椭圆节点形状?。这样做的好处是可以非常精确地计算气球形状中的锚点,从而简化后续的连接。
  2. 我没有将 Jake 的所有代码(太长了!)都放在主文件中,而是将其分离到一个tikz-superellipse.sty文件中(代码如下)。这定义了一个名为 的新节点形状superellipse
  3. 创建了一种balloon样式,它接受气球的宽度作为参数。
  4. 文本默认居中
  5. 使用了漂亮的漫画字体(下载自bancomicsans 页面,即“Mighty Zeo”,大写版本)。需要xelatex使用此字体进行编译。

这是新代码:

\documentclass{standalone}
\usepackage{tikz}
\usepackage{fontspec}
\usepackage{tikz-superellipse}
\setmainfont{MIGHZC__.TTF}
\usetikzlibrary{calc,positioning}

\tikzset{
  connection/.style = {line width=2mm, white, shorten >=-6pt, shorten <=-6pt,
     preaction={draw, black, line width=3mm, shorten >=-3pt, shorten <=-3pt}
  },
  balloon/.style = {text width=#1, inner sep=.5em, align=center, superellipse,
  superellipse parameter=3.5, very thick},
  balloon/.default = {11em}
}
\begin{document}

\begin{tikzpicture}
\node[balloon=7em]
(balloon1) {Lorem ipsum dolor sit amet, consectetur adipisicing elit,
            sed do eiusmod tempor};
\node[balloon, right=of balloon1.north east]
(balloon2) {incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,};

\node[balloon=12em, below=of balloon2]
(balloon3) {quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat};

\draw[connection] (balloon1.north east) to [out=20, in=180] (balloon2.west);
\draw[connection] (balloon2.south) -- (balloon3.north);

\node[fill=white, minimum width=3mm, minimum height=1mm, inner sep=0] at (balloon3.south) (callout) {};
\draw[very thick] (callout.north west) to[bend left] +(-5mm,-1cm) to[bend right] (callout.north east);

\end{tikzpicture}
\end{document}

结果如下:

结果

这是tikz-superellipse.sty文件:

\let\pgfmath@function@exp\relax % undefine old exp function
\pgfmathdeclarefunction{exp}{1}{%   
  \begingroup
    \pgfmath@xc=#1pt\relax
    \pgfmath@yc=#1pt\relax
    \ifdim\pgfmath@xc<-9pt
      \pgfmath@x=1sp\relax
    \else
      \ifdim\pgfmath@xc<0pt
        \pgfmath@xc=-\pgfmath@xc
      \fi
      \pgfmath@x=1pt\relax
      \pgfmath@xa=1pt\relax
      \pgfmath@xb=\pgfmath@x
      \pgfmathloop%
        \divide\pgfmath@xa by\pgfmathcounter
        \pgfmath@xa=\pgfmath@tonumber\pgfmath@xc\pgfmath@xa%
        \advance\pgfmath@x by\pgfmath@xa
      \ifdim\pgfmath@x=\pgfmath@xb
      \else
        \pgfmath@xb=\pgfmath@x
      \repeatpgfmathloop%
      \ifdim\pgfmath@yc<0pt
        \pgfmathreciprocal@{\pgfmath@tonumber\pgfmath@x}%
        \pgfmath@x=\pgfmathresult pt\relax
      \fi
    \fi
    \pgfmath@returnone\pgfmath@x%
  \endgroup
}

\let\pgfmath@function@pow\relax % undefine old exp function
\pgfmathdeclarefunction{pow}{2}{%
  \begingroup%
    \pgfmath@xa=#1pt%
    \pgfmath@xb=#2pt%
    \ifdim\pgfmath@xa=0pt
        \pgfmath@x=0pt\relax
    \else
    \afterassignment\pgfmath@x%
        \expandafter\c@pgfmath@counta\the\pgfmath@xb\relax%
        \ifnum\c@pgfmath@counta<0\relax%
            \c@pgfmath@counta=-\c@pgfmath@counta%
            \pgfmathreciprocal@{#1}%
            \pgfmath@xa=\pgfmathresult pt\relax%
        \fi
        \ifdim\pgfmath@x=0pt\relax%
            \pgfmath@x=1pt\relax%
            \pgfmathloop%
                \ifnum\c@pgfmath@counta>0\relax%
                    \ifodd\c@pgfmath@counta%
 \pgfmath@x=\pgfmath@tonumber{\pgfmath@x}\pgfmath@xa%
                    \fi
                    \ifnum\c@pgfmath@counta>1\relax%
 \pgfmath@xa=\pgfmath@tonumber{\pgfmath@xa}\pgfmath@xa%
                    \fi%
                    \divide\c@pgfmath@counta by2\relax%
            \repeatpgfmathloop%
        \else%
            \pgfmathln@{#1}%
            \pgfmath@x=\pgfmathresult pt\relax%
            \pgfmath@x=\pgfmath@tonumber{\pgfmath@xb}\pgfmath@x%
            \pgfmathexp@{\pgfmath@tonumber{\pgfmath@x}}%
            \pgfmath@x=\pgfmathresult pt\relax%
        \fi%
    \fi
    \pgfmath@returnone\pgf@x%
    \endgroup%
}

\pgfkeys{
    /pgf/superellipse parameter/.store in=\pgf@superellipse@param,
    /pgf/superellipse parameter/.default=2,
    /pgf/superellipse parameter
}

\newcommand{\pointonsuperellipse}[3]{ % cornerpoint, parameter, directionpoint
    \pgf@process{#1}
    \edef\size@x{\the\pgf@x}%
    \edef\size@y{\the\pgf@y}%
    \pgfintersectionofpaths
        {
            \pgfpathmoveto{\centerpoint}
            \pgfpathlineto{
                \pgfpointborderrectangle{#3}{#1}
            }
            \pgfpathclose
        }
        {
            \pgfplothandlercurveto
            \pgfplotfunction{\x}{-180,-170,...,170}{
                \pgfpoint{
                    abs(1 * cos(\x))^(2/#2)*( (cos(\x)>0)*2-1 ) * \size@x
                }{
                    abs(1 * sin(\x))^(2/#2)*( (sin(\x)>0)*2-1 ) * \size@y
                }
        }
        \pgfpathclose
    }
    \pgfpointintersectionsolution{1}
}

\makeatletter
\pgfdeclareshape{superellipse}
%
% Draws a circle around the text
%
{
\savedmacro\superellipseparameter{\edef\superellipseparameter{\pgf@superellipse@param}}
  \savedanchor\centerpoint{%
    \pgf@x=.5\wd\pgfnodeparttextbox%
    \pgf@y=.5\ht\pgfnodeparttextbox%
    \advance\pgf@y by-.5\dp\pgfnodeparttextbox%
  }
  \savedanchor\radius{%
    %
    % Caculate ``height radius''
    %
    \pgf@y=.5\ht\pgfnodeparttextbox%
    \advance\pgf@y by.5\dp\pgfnodeparttextbox%
    \pgfmathsetlength\pgf@yb{\pgfkeysvalueof{/pgf/inner ysep}}%
    \advance\pgf@y by\pgf@yb%
    %
    % Caculate ``width radius''
    %
    \pgf@x=.5\wd\pgfnodeparttextbox%
    \pgfmathsetlength\pgf@xb{\pgfkeysvalueof{/pgf/inner xsep}}%
    \advance\pgf@x by\pgf@xb%
    %
    % Adjust
    %
    \pgf@x=1.4142136\pgf@x%
    \pgf@y=1.4142136\pgf@y%
    %
    % Adjust hieght, if necessary
    %
    \pgfmathsetlength\pgf@yc{\pgfkeysvalueof{/pgf/minimum height}}%
    \ifdim\pgf@y<.5\pgf@yc%
      \pgf@y=.5\pgf@yc%
    \fi%
    %
    % Adjust width, if necessary
    %
    \pgfmathsetlength\pgf@xc{\pgfkeysvalueof{/pgf/minimum width}}%
    \ifdim\pgf@x<.5\pgf@xc%
      \pgf@x=.5\pgf@xc%
    \fi%
    %
    % Add outer sep
    %
    \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}%
    \pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}%
    \advance\pgf@x by\pgf@xb%
    \advance\pgf@y by\pgf@yb%
  }
  \savedmacro\test{\def\test{2}}

  %
  % Anchors
  %
  \anchor{center}{\centerpoint}
  \anchor{mid}{\centerpoint\pgfmathsetlength\pgf@y{.5ex}}
  \anchor{base}{\centerpoint\pgf@y=0pt}
  \anchor{north}
  {
    \pgf@process{\radius}
    \pgf@ya=\pgf@y%
    \pgf@process{\centerpoint}
    \advance\pgf@y by\pgf@ya
  }
  \anchor{south}
  {
    \pgf@process{\radius}
    \pgf@ya=\pgf@y%
    \pgf@process{\centerpoint}
    \advance\pgf@y by-\pgf@ya
  }
  \anchor{west}
  {
    \pgf@process{\radius}
    \pgf@xa=\pgf@x%
    \pgf@process{\centerpoint}
    \advance\pgf@x by-\pgf@xa
  }
  \anchor{mid west}
  {%
    \pgf@process{\radius}
    \pgf@xa=\pgf@x%
    \pgf@process{\centerpoint}
    \advance\pgf@x by-\pgf@xa%
    \pgfmathsetlength\pgf@y{.5ex}
  }
  \anchor{base west}
  {%
    \pgf@process{\radius}
    \pgf@xa=\pgf@x%
    \pgf@process{\centerpoint}
    \advance\pgf@x by-\pgf@xa%
    \pgf@y=0pt
  }
  \anchor{north west}
  {
    \pgf@process{\radius}
    \def\angle{135}
    \pgf@xa=\pgf@x%
    \pgf@ya=\pgf@y%  
    \pgf@process{\pgfpoint{
        abs(cos(\angle))^(2/\superellipseparameter)*( (cos(\angle)>0)*2-1 ) * \pgf@xa
     }{
        abs(sin(\angle))^(2/\superellipseparameter)*( (sin(\angle)>0)*2-1 ) * \pgf@ya
    }}
    \pgf@xb=\pgf@x%
    \pgf@yb=\pgf@y%  
     \pgf@process{\centerpoint}
    \advance\pgf@x by \pgf@xb
    \advance\pgf@y by \pgf@yb
  }
  \anchor{south west}
  {
    \pgf@process{\radius}
    \def\angle{-135}
    \pgf@xa=\pgf@x%
    \pgf@ya=\pgf@y%  
    \pgf@process{\pgfpoint{
        abs(cos(\angle))^(2/\superellipseparameter)*( (cos(\angle)>0)*2-1 ) * \pgf@xa
     }{
        abs(sin(\angle))^(2/\superellipseparameter)*( (sin(\angle)>0)*2-1 ) * \pgf@ya
    }}
    \pgf@xb=\pgf@x%
    \pgf@yb=\pgf@y%  
     \pgf@process{\centerpoint}
    \advance\pgf@x by \pgf@xb
    \advance\pgf@y by \pgf@yb
  }
  \anchor{east}
  {%
    \pgf@process{\radius}
    \pgf@xa=\pgf@x%
    \pgf@process{\centerpoint}
    \advance\pgf@x by\pgf@xa
  }
  \anchor{mid east}
  {%
    \pgf@process{\radius}
    \pgf@xa=\pgf@x%
    \pgf@process{\centerpoint}
    \advance\pgf@x by\pgf@xa%
    \pgfmathsetlength\pgf@y{.5ex}
  }
  \anchor{base east}
  {%
    \pgf@process{\radius}
    \pgf@xa=\pgf@x%
    \pgf@process{\centerpoint}
    \advance\pgf@x by\pgf@xa%
    \pgf@y=0pt
  }
  \anchor{north east}
  {
    \pgf@process{\radius}
    \def\angle{45}
    \pgf@xa=\pgf@x%
    \pgf@ya=\pgf@y%  
    \pgf@process{\pgfpoint{
        abs(cos(\angle))^(2/\superellipseparameter)*( (cos(\angle)>0)*2-1 ) * \pgf@xa
     }{
        abs(sin(\angle))^(2/\superellipseparameter)*( (sin(\angle)>0)*2-1 ) * \pgf@ya
    }}
    \pgf@xb=\pgf@x%
    \pgf@yb=\pgf@y%  
     \pgf@process{\centerpoint}
    \advance\pgf@x by \pgf@xb
    \advance\pgf@y by \pgf@yb
  }
  \anchor{south east}
  {
    \pgf@process{\radius}
    \def\angle{-45}
    \pgf@xa=\pgf@x%
    \pgf@ya=\pgf@y%  
    \pgf@process{\pgfpoint{
        abs(cos(\angle))^(2/\superellipseparameter)*( (cos(\angle)>0)*2-1 ) * \pgf@xa
     }{
        abs(sin(\angle))^(2/\superellipseparameter)*( (sin(\angle)>0)*2-1 ) * \pgf@ya
    }}
    \pgf@xb=\pgf@x%
    \pgf@yb=\pgf@y%  
     \pgf@process{\centerpoint}
    \advance\pgf@x by \pgf@xb
    \advance\pgf@y by \pgf@yb
  }
  \anchorborder{
    \edef\externalx{\the\pgf@x}%
    \edef\externaly{\the\pgf@y}%
    \pgf@process{\radius}%
    \pgf@xa=\pgf@x%
    \pgf@ya=\pgf@y%
    \pointonsuperellipse{\pgfpoint{\pgf@xa}{\pgf@ya}}{\superellipseparameter}{\pgfpoint{\externalx}{\externaly}}
    \pgf@xa=\pgf@x%
    \pgf@ya=\pgf@y%
    \centerpoint%
    \advance\pgf@x by\pgf@xa%
    \advance\pgf@y by\pgf@ya%
  }


  \backgroundpath
  {
    \pgf@process{\radius}%
    \pgfutil@tempdima=\pgf@x%
    \pgfutil@tempdimb=\pgf@y%
    \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}%
    \pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}%
    \advance\pgfutil@tempdima by-\pgf@xb%
    \advance\pgfutil@tempdimb by-\pgf@yb%
    {
        \pgftransformshift{\centerpoint}
        \pgfplothandlercurveto
        \pgfplotfunction{\x}{-180,-170,...,170}{
            \pgfpoint{
                abs(1 * cos(\x))^(2/\pgf@superellipse@param)*( (cos(\x)>0)*2-1 ) * \pgfutil@tempdima
            }{
                abs(1 * sin(\x))^(2/\pgf@superellipse@param)*( (sin(\x)>0)*2-1 ) * \pgfutil@tempdimb
            }
        }
        \pgfpathclose
        \pgfgetpath\test
        \pgfusepath{stroke}
    }
  }
}

答案2

你始终可以手动完成:

\documentclass[tikz,border=2mm]{standalone}
\usetikzlibrary{positioning}
\begin{document}

\begin{tikzpicture}

\node[fill=green!30,minimum width=2cm,minimum height=3cm] (a) {A};
\node[fill=blue!30,minimum width=3cm,minimum height=3cm,right=1cm of a] (b) {B};
\node[fill=orange!30,minimum width=6cm,minimum height=3cm, below=of b.south east, anchor=north east] (c) {C};
\node[fill=red!30,minimum width=2cm,minimum height=3cm, below=of c.south east, anchor=north east] (d) {D};

\draw[thick] (a.-15)|-(a.south west) |-(a.north east)--(a.3)--(a.3-|b.west)|-(b.north east)|-(b.273)--(b.273|-c.north)-|(c.south east)--(c.-32)--(c.-32|-d.north) -|(d.south east)-|(d.north west)--(d.96)--(d.96|-c.south)-|(c.north west)--(c.55)--(c.55|-b.south)-|(a.-15-|b.west)--(a.-15)--cycle;
\end{tikzpicture}

\end{document}

在此处输入图片描述

更新:

根据 szantaii 的建议,下图显示了一些上一个图decoration

\documentclass[tikz,border=2mm]{standalone}
\usetikzlibrary{positioning,decorations.pathmorphing,backgrounds}
\begin{document}

\begin{tikzpicture}

\node[minimum width=2cm,minimum height=3cm] (a) {A};
\node[minimum width=3cm,minimum height=3cm,right=1cm of a] (b) {B};
\node[minimum width=6cm,minimum height=3cm, below=of b.south east, anchor=north east] (c) {C};
\node[minimum width=2cm,minimum height=3cm, below=of c.south east, anchor=north east] (d) {D};

\begin{scope}[on background layer]
\draw[rounded corners=1mm, thick, fill=purple!30, decoration={random steps, segment length=3pt, amplitude=0.5pt},decorate] (a.-15)|-(a.south west) |-(a.north east)--(a.3)--(a.3-|b.west)|-(b.north east)|-(b.273)--(b.273|-c.north)-|(c.south east)--(c.-32)--(c.-32|-d.north) -|(d.south east)-|(d.north west)--(d.96)--(d.96|-c.south)-|(c.north west)--(c.55)--(c.55|-b.south)-|(a.-15-|b.west)--(a.-15)--cycle;
\end{scope}
\end{tikzpicture}

\end{document}

在此处输入图片描述

虽然这个解决方案不如 JLDiaz 那么好,但它不需要使用white(或任何其他颜色)线来模拟double线条。

相关内容