A long time ago there was an export backend for org that defined
org-export-publishing-directory, but it disappeared when the export code was rewritten. It turns out that the export functions can still do it with a little help. Here's how I set things up by advising an org function:
;; Add advice to let me set a different directory for the exported
;; files. Usually I do this with a directory-local variable for a
;; project, like this:
;; ((org-mode . ((my/org-export-dir "export-dir"))))
(defun my/set-org-export-directory (orig-fun extension
&optional subtreep pub-dir)
"Advice function to set export directory for `org-export-output-file-name'.
Set `my/org-export-dir' to specify the desired export directory."
(let ((pub-dir (or pub-dir
(and (boundp 'my/org-export-dir)
(apply orig-fun extension subtreep pub-dir nil)))
Of course, this is fragile with respect to changes in the org-mode code!